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 2015/01/23 17:41:28 UTC

[06/15] syncope git commit: FIT server integration tests

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractPushResultHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractPushResultHandler.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractPushResultHandler.java
new file mode 100644
index 0000000..74cd5f0
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractPushResultHandler.java
@@ -0,0 +1,374 @@
+/*
+ * 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.server.provisioning.java.sync;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.apache.syncope.common.lib.mod.AttrMod;
+import org.apache.syncope.common.lib.mod.MembershipMod;
+import org.apache.syncope.common.lib.to.AbstractSubjectTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.AuditElements.Result;
+import org.apache.syncope.common.lib.types.IntMappingType;
+import org.apache.syncope.common.lib.types.MatchingRule;
+import org.apache.syncope.common.lib.types.PropagationByResource;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.common.lib.types.UnmatchingRule;
+import org.apache.syncope.server.persistence.api.entity.AttributableUtil;
+import org.apache.syncope.server.persistence.api.entity.Mapping;
+import org.apache.syncope.server.persistence.api.entity.MappingItem;
+import org.apache.syncope.server.persistence.api.entity.Subject;
+import org.apache.syncope.server.persistence.api.entity.VirAttr;
+import org.apache.syncope.server.persistence.api.entity.membership.Membership;
+import org.apache.syncope.server.persistence.api.entity.task.PushTask;
+import org.apache.syncope.server.persistence.api.entity.user.User;
+import org.apache.syncope.server.provisioning.api.sync.ProvisioningResult;
+import org.apache.syncope.server.provisioning.api.sync.PushActions;
+import org.apache.syncope.server.misc.MappingUtil;
+import org.apache.syncope.server.provisioning.api.sync.SyncopePushResultHandler;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.quartz.JobExecutionException;
+import org.springframework.transaction.annotation.Transactional;
+
+public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHandler<PushTask, PushActions>
+        implements SyncopePushResultHandler {
+
+    protected abstract String getName(final Subject<?, ?, ?> subject);
+
+    protected abstract Mapping<?> getMapping();
+
+    protected abstract AbstractSubjectTO getSubjectTO(final long key);
+
+    protected abstract Subject<?, ?, ?> getSubject(final long key);
+
+    protected abstract Subject<?, ?, ?> deprovision(final Subject<?, ?, ?> sbj);
+
+    protected abstract Subject<?, ?, ?> provision(final Subject<?, ?, ?> sbj, final Boolean enabled);
+
+    protected abstract Subject<?, ?, ?> link(final Subject<?, ?, ?> sbj, final Boolean unlink);
+
+    protected abstract Subject<?, ?, ?> unassign(final Subject<?, ?, ?> sbj);
+
+    protected abstract Subject<?, ?, ?> assign(final Subject<?, ?, ?> sbj, Boolean enabled);
+
+    protected abstract ConnectorObject getRemoteObject(final String accountId);
+
+    @Transactional
+    @Override
+    public boolean handle(final long subjectId) {
+        try {
+            doHandle(subjectId);
+            return true;
+        } catch (JobExecutionException e) {
+            LOG.error("Synchronization failed", e);
+            return false;
+        }
+    }
+
+    protected final void doHandle(final long subjectId)
+            throws JobExecutionException {
+
+        final Subject<?, ?, ?> subject = getSubject(subjectId);
+
+        final AttributableUtil attrUtil = attrUtilFactory.getInstance(subject);
+
+        final ProvisioningResult result = new ProvisioningResult();
+        profile.getResults().add(result);
+
+        result.setId(subject.getKey());
+        result.setSubjectType(attrUtil.getType());
+        result.setName(getName(subject));
+
+        final Boolean enabled = subject instanceof User && profile.getTask().isSyncStatus()
+                ? ((User) subject).isSuspended() ? Boolean.FALSE : Boolean.TRUE
+                : null;
+
+        LOG.debug("Propagating {} with key {} towards {}",
+                attrUtil.getType(), subject.getKey(), profile.getTask().getResource());
+
+        Object output = null;
+        Result resultStatus = null;
+        ConnectorObject beforeObj = null;
+        String operation = null;
+
+        // Try to read remote object (user / group) BEFORE any actual operation
+        final String accountId = MappingUtil.getAccountIdValue(
+                subject, profile.getTask().getResource(), getMapping().getAccountIdItem());
+
+        beforeObj = getRemoteObject(accountId);
+
+        Boolean status = profile.getTask().isSyncStatus() ? enabled : null;
+
+        if (profile.isDryRun()) {
+            if (beforeObj == null) {
+                result.setOperation(getResourceOperation(profile.getTask().getUnmatchingRule()));
+            } else {
+                result.setOperation(getResourceOperation(profile.getTask().getMatchingRule()));
+            }
+            result.setStatus(ProvisioningResult.Status.SUCCESS);
+        } else {
+            try {
+                if (beforeObj == null) {
+                    operation = profile.getTask().getUnmatchingRule().name().toLowerCase();
+                    result.setOperation(getResourceOperation(profile.getTask().getUnmatchingRule()));
+
+                    switch (profile.getTask().getUnmatchingRule()) {
+                        case ASSIGN:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeAssign(this.getProfile(), subject);
+                            }
+
+                            if (!profile.getTask().isPerformCreate()) {
+                                LOG.debug("PushTask not configured for create");
+                            } else {
+                                assign(subject, status);
+                            }
+
+                            break;
+                        case PROVISION:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeProvision(this.getProfile(), subject);
+                            }
+
+                            if (!profile.getTask().isPerformCreate()) {
+                                LOG.debug("PushTask not configured for create");
+                            } else {
+                                provision(subject, status);
+                            }
+
+                            break;
+                        case UNLINK:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeUnlink(this.getProfile(), subject);
+                            }
+
+                            if (!profile.getTask().isPerformUpdate()) {
+                                LOG.debug("PushTask not configured for update");
+                            } else {
+                                link(subject, true);
+                            }
+
+                            break;
+                        default:
+                        // do nothing
+                    }
+
+                } else {
+                    operation = profile.getTask().getMatchingRule().name().toLowerCase();
+                    result.setOperation(getResourceOperation(profile.getTask().getMatchingRule()));
+
+                    switch (profile.getTask().getMatchingRule()) {
+                        case UPDATE:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeUpdate(this.getProfile(), subject);
+                            }
+                            if (!profile.getTask().isPerformUpdate()) {
+                                LOG.debug("PushTask not configured for update");
+                            } else {
+                                update(subject, status);
+                            }
+
+                            break;
+                        case DEPROVISION:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeDeprovision(this.getProfile(), subject);
+                            }
+
+                            if (!profile.getTask().isPerformDelete()) {
+                                LOG.debug("PushTask not configured for delete");
+                            } else {
+                                deprovision(subject);
+                            }
+
+                            break;
+                        case UNASSIGN:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeUnassign(this.getProfile(), subject);
+                            }
+
+                            if (!profile.getTask().isPerformDelete()) {
+                                LOG.debug("PushTask not configured for delete");
+                            } else {
+                                unassign(subject);
+                            }
+
+                            break;
+                        case LINK:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeLink(this.getProfile(), subject);
+                            }
+
+                            if (!profile.getTask().isPerformUpdate()) {
+                                LOG.debug("PushTask not configured for update");
+                            } else {
+                                link(subject, false);
+                            }
+
+                            break;
+                        case UNLINK:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeUnlink(this.getProfile(), subject);
+                            }
+
+                            if (!profile.getTask().isPerformUpdate()) {
+                                LOG.debug("PushTask not configured for update");
+                            } else {
+                                link(subject, true);
+                            }
+
+                            break;
+                        default:
+                        // do nothing
+                    }
+                }
+
+                for (PushActions action : profile.getActions()) {
+                    action.after(this.getProfile(), subject, result);
+                }
+
+                result.setStatus(ProvisioningResult.Status.SUCCESS);
+                resultStatus = AuditElements.Result.SUCCESS;
+                output = getRemoteObject(accountId);
+            } catch (Exception e) {
+                result.setStatus(ProvisioningResult.Status.FAILURE);
+                result.setMessage(ExceptionUtils.getRootCauseMessage(e));
+                resultStatus = AuditElements.Result.FAILURE;
+                output = e;
+
+                LOG.warn("Error pushing {} towards {}", subject, profile.getTask().getResource(), e);
+                throw new JobExecutionException(e);
+            } finally {
+                notificationManager.createTasks(
+                        AuditElements.EventCategoryType.PUSH,
+                        AttributableType.USER.name().toLowerCase(),
+                        profile.getTask().getResource().getKey(),
+                        operation,
+                        resultStatus,
+                        beforeObj,
+                        output,
+                        subject);
+                auditManager.audit(
+                        AuditElements.EventCategoryType.PUSH,
+                        AttributableType.USER.name().toLowerCase(),
+                        profile.getTask().getResource().getKey(),
+                        operation,
+                        resultStatus,
+                        beforeObj,
+                        output,
+                        subject);
+            }
+        }
+    }
+
+    private ResourceOperation getResourceOperation(final UnmatchingRule rule) {
+        switch (rule) {
+            case ASSIGN:
+            case PROVISION:
+                return ResourceOperation.CREATE;
+            default:
+                return ResourceOperation.NONE;
+        }
+    }
+
+    private ResourceOperation getResourceOperation(final MatchingRule rule) {
+        switch (rule) {
+            case UPDATE:
+                return ResourceOperation.UPDATE;
+            case DEPROVISION:
+            case UNASSIGN:
+                return ResourceOperation.DELETE;
+            default:
+                return ResourceOperation.NONE;
+        }
+    }
+
+    protected Subject<?, ?, ?> update(final Subject<?, ?, ?> sbj, final Boolean enabled) {
+
+        final Set<MembershipMod> membsToAdd = new HashSet<>();
+        final Set<String> vattrToBeRemoved = new HashSet<>();
+        final Set<String> membVattrToBeRemoved = new HashSet<>();
+        final Set<AttrMod> vattrToBeUpdated = new HashSet<>();
+
+        // Search for all mapped vattrs
+        final Mapping<?> umapping = getMapping();
+        for (MappingItem mappingItem : umapping.getItems()) {
+            if (mappingItem.getIntMappingType() == IntMappingType.UserVirtualSchema) {
+                vattrToBeRemoved.add(mappingItem.getIntAttrName());
+            } else if (mappingItem.getIntMappingType() == IntMappingType.MembershipVirtualSchema) {
+                membVattrToBeRemoved.add(mappingItem.getIntAttrName());
+            }
+        }
+
+        // Search for all user's vattrs and:
+        // 1. add mapped vattrs not owned by the user to the set of vattrs to be removed
+        // 2. add all vattrs owned by the user to the set of vattrs to be update
+        for (VirAttr vattr : sbj.getVirAttrs()) {
+            vattrToBeRemoved.remove(vattr.getSchema().getKey());
+            final AttrMod mod = new AttrMod();
+            mod.setSchema(vattr.getSchema().getKey());
+            mod.getValuesToBeAdded().addAll(vattr.getValues());
+            vattrToBeUpdated.add(mod);
+        }
+
+        final boolean changepwd;
+
+        if (sbj instanceof User) {
+            changepwd = true;
+
+            // Search for memberships
+            for (Membership membership : User.class.cast(sbj).getMemberships()) {
+                final MembershipMod membershipMod = new MembershipMod();
+                membershipMod.setKey(membership.getKey());
+                membershipMod.setRole(membership.getRole().getKey());
+
+                for (VirAttr vattr : membership.getVirAttrs()) {
+                    membVattrToBeRemoved.remove(vattr.getSchema().getKey());
+                    final AttrMod mod = new AttrMod();
+                    mod.setSchema(vattr.getSchema().getKey());
+                    mod.getValuesToBeAdded().addAll(vattr.getValues());
+                    membershipMod.getVirAttrsToUpdate().add(mod);
+                }
+
+                membsToAdd.add(membershipMod);
+            }
+
+            if (!membsToAdd.isEmpty()) {
+                membsToAdd.iterator().next().getVirAttrsToRemove().addAll(membVattrToBeRemoved);
+            }
+        } else {
+            changepwd = false;
+        }
+
+        final List<String> noPropResources = new ArrayList<>(sbj.getResourceNames());
+        noPropResources.remove(profile.getTask().getResource().getKey());
+
+        final PropagationByResource propByRes = new PropagationByResource();
+        propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
+
+        taskExecutor.execute(propagationManager.getUpdateTaskIds(
+                sbj, null, changepwd, enabled, vattrToBeRemoved, vattrToBeUpdated, propByRes, noPropResources,
+                membsToAdd));
+
+        return userDAO.authFetch(sbj.getKey());
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSubjectPushResultHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSubjectPushResultHandler.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSubjectPushResultHandler.java
deleted file mode 100644
index f0b70cd..0000000
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSubjectPushResultHandler.java
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- * 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.server.provisioning.java.sync;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import org.apache.commons.lang3.exception.ExceptionUtils;
-import org.apache.syncope.common.lib.mod.AttrMod;
-import org.apache.syncope.common.lib.mod.MembershipMod;
-import org.apache.syncope.common.lib.to.AbstractSubjectTO;
-import org.apache.syncope.common.lib.types.AttributableType;
-import org.apache.syncope.common.lib.types.AuditElements;
-import org.apache.syncope.common.lib.types.AuditElements.Result;
-import org.apache.syncope.common.lib.types.IntMappingType;
-import org.apache.syncope.common.lib.types.MatchingRule;
-import org.apache.syncope.common.lib.types.PropagationByResource;
-import org.apache.syncope.common.lib.types.ResourceOperation;
-import org.apache.syncope.common.lib.types.UnmatchingRule;
-import org.apache.syncope.server.persistence.api.entity.AttributableUtil;
-import org.apache.syncope.server.persistence.api.entity.Mapping;
-import org.apache.syncope.server.persistence.api.entity.MappingItem;
-import org.apache.syncope.server.persistence.api.entity.Subject;
-import org.apache.syncope.server.persistence.api.entity.VirAttr;
-import org.apache.syncope.server.persistence.api.entity.membership.Membership;
-import org.apache.syncope.server.persistence.api.entity.task.PushTask;
-import org.apache.syncope.server.persistence.api.entity.user.User;
-import org.apache.syncope.server.provisioning.api.sync.ProvisioningResult;
-import org.apache.syncope.server.provisioning.api.sync.PushActions;
-import org.apache.syncope.server.misc.MappingUtil;
-import org.identityconnectors.framework.common.objects.ConnectorObject;
-import org.quartz.JobExecutionException;
-import org.springframework.transaction.annotation.Transactional;
-
-public abstract class AbstractSubjectPushResultHandler extends AbstractSyncopeResultHandler<PushTask, PushActions> {
-
-    protected abstract String getName(final Subject<?, ?, ?> subject);
-
-    protected abstract Mapping<?> getMapping();
-
-    protected abstract AbstractSubjectTO getSubjectTO(final long key);
-
-    protected abstract Subject<?, ?, ?> getSubject(final long key);
-
-    protected abstract Subject<?, ?, ?> deprovision(final Subject<?, ?, ?> sbj);
-
-    protected abstract Subject<?, ?, ?> provision(final Subject<?, ?, ?> sbj, final Boolean enabled);
-
-    protected abstract Subject<?, ?, ?> link(final Subject<?, ?, ?> sbj, final Boolean unlink);
-
-    protected abstract Subject<?, ?, ?> unassign(final Subject<?, ?, ?> sbj);
-
-    protected abstract Subject<?, ?, ?> assign(final Subject<?, ?, ?> sbj, Boolean enabled);
-
-    protected abstract ConnectorObject getRemoteObject(final String accountId);
-
-    @Transactional
-    public boolean handle(final long subjectId) {
-        try {
-            doHandle(subjectId);
-            return true;
-        } catch (JobExecutionException e) {
-            LOG.error("Synchronization failed", e);
-            return false;
-        }
-    }
-
-    protected final void doHandle(final long subjectId)
-            throws JobExecutionException {
-
-        final Subject<?, ?, ?> subject = getSubject(subjectId);
-
-        final AttributableUtil attrUtil = attrUtilFactory.getInstance(subject);
-
-        final ProvisioningResult result = new ProvisioningResult();
-        profile.getResults().add(result);
-
-        result.setId(subject.getKey());
-        result.setSubjectType(attrUtil.getType());
-        result.setName(getName(subject));
-
-        final Boolean enabled = subject instanceof User && profile.getTask().isSyncStatus()
-                ? ((User) subject).isSuspended() ? Boolean.FALSE : Boolean.TRUE
-                : null;
-
-        LOG.debug("Propagating {} with key {} towards {}",
-                attrUtil.getType(), subject.getKey(), profile.getTask().getResource());
-
-        Object output = null;
-        Result resultStatus = null;
-        ConnectorObject beforeObj = null;
-        String operation = null;
-
-        // Try to read remote object (user / group) BEFORE any actual operation
-        final String accountId = MappingUtil.getAccountIdValue(
-                subject, profile.getTask().getResource(), getMapping().getAccountIdItem());
-
-        beforeObj = getRemoteObject(accountId);
-
-        Boolean status = profile.getTask().isSyncStatus() ? enabled : null;
-
-        if (profile.isDryRun()) {
-            if (beforeObj == null) {
-                result.setOperation(getResourceOperation(profile.getTask().getUnmatchingRule()));
-            } else {
-                result.setOperation(getResourceOperation(profile.getTask().getMatchingRule()));
-            }
-            result.setStatus(ProvisioningResult.Status.SUCCESS);
-        } else {
-            try {
-                if (beforeObj == null) {
-                    operation = profile.getTask().getUnmatchingRule().name().toLowerCase();
-                    result.setOperation(getResourceOperation(profile.getTask().getUnmatchingRule()));
-
-                    switch (profile.getTask().getUnmatchingRule()) {
-                        case ASSIGN:
-                            for (PushActions action : profile.getActions()) {
-                                action.beforeAssign(this.getProfile(), subject);
-                            }
-
-                            if (!profile.getTask().isPerformCreate()) {
-                                LOG.debug("PushTask not configured for create");
-                            } else {
-                                assign(subject, status);
-                            }
-
-                            break;
-                        case PROVISION:
-                            for (PushActions action : profile.getActions()) {
-                                action.beforeProvision(this.getProfile(), subject);
-                            }
-
-                            if (!profile.getTask().isPerformCreate()) {
-                                LOG.debug("PushTask not configured for create");
-                            } else {
-                                provision(subject, status);
-                            }
-
-                            break;
-                        case UNLINK:
-                            for (PushActions action : profile.getActions()) {
-                                action.beforeUnlink(this.getProfile(), subject);
-                            }
-
-                            if (!profile.getTask().isPerformUpdate()) {
-                                LOG.debug("PushTask not configured for update");
-                            } else {
-                                link(subject, true);
-                            }
-
-                            break;
-                        default:
-                        // do nothing
-                    }
-
-                } else {
-                    operation = profile.getTask().getMatchingRule().name().toLowerCase();
-                    result.setOperation(getResourceOperation(profile.getTask().getMatchingRule()));
-
-                    switch (profile.getTask().getMatchingRule()) {
-                        case UPDATE:
-                            for (PushActions action : profile.getActions()) {
-                                action.beforeUpdate(this.getProfile(), subject);
-                            }
-                            if (!profile.getTask().isPerformUpdate()) {
-                                LOG.debug("PushTask not configured for update");
-                            } else {
-                                update(subject, status);
-                            }
-
-                            break;
-                        case DEPROVISION:
-                            for (PushActions action : profile.getActions()) {
-                                action.beforeDeprovision(this.getProfile(), subject);
-                            }
-
-                            if (!profile.getTask().isPerformDelete()) {
-                                LOG.debug("PushTask not configured for delete");
-                            } else {
-                                deprovision(subject);
-                            }
-
-                            break;
-                        case UNASSIGN:
-                            for (PushActions action : profile.getActions()) {
-                                action.beforeUnassign(this.getProfile(), subject);
-                            }
-
-                            if (!profile.getTask().isPerformDelete()) {
-                                LOG.debug("PushTask not configured for delete");
-                            } else {
-                                unassign(subject);
-                            }
-
-                            break;
-                        case LINK:
-                            for (PushActions action : profile.getActions()) {
-                                action.beforeLink(this.getProfile(), subject);
-                            }
-
-                            if (!profile.getTask().isPerformUpdate()) {
-                                LOG.debug("PushTask not configured for update");
-                            } else {
-                                link(subject, false);
-                            }
-
-                            break;
-                        case UNLINK:
-                            for (PushActions action : profile.getActions()) {
-                                action.beforeUnlink(this.getProfile(), subject);
-                            }
-
-                            if (!profile.getTask().isPerformUpdate()) {
-                                LOG.debug("PushTask not configured for update");
-                            } else {
-                                link(subject, true);
-                            }
-
-                            break;
-                        default:
-                        // do nothing
-                    }
-                }
-
-                for (PushActions action : profile.getActions()) {
-                    action.after(this.getProfile(), subject, result);
-                }
-
-                result.setStatus(ProvisioningResult.Status.SUCCESS);
-                resultStatus = AuditElements.Result.SUCCESS;
-                output = getRemoteObject(accountId);
-            } catch (Exception e) {
-                result.setStatus(ProvisioningResult.Status.FAILURE);
-                result.setMessage(ExceptionUtils.getRootCauseMessage(e));
-                resultStatus = AuditElements.Result.FAILURE;
-                output = e;
-
-                LOG.warn("Error pushing {} towards {}", subject, profile.getTask().getResource(), e);
-                throw new JobExecutionException(e);
-            } finally {
-                notificationManager.createTasks(
-                        AuditElements.EventCategoryType.PUSH,
-                        AttributableType.USER.name().toLowerCase(),
-                        profile.getTask().getResource().getKey(),
-                        operation,
-                        resultStatus,
-                        beforeObj,
-                        output,
-                        subject);
-                auditManager.audit(
-                        AuditElements.EventCategoryType.PUSH,
-                        AttributableType.USER.name().toLowerCase(),
-                        profile.getTask().getResource().getKey(),
-                        operation,
-                        resultStatus,
-                        beforeObj,
-                        output,
-                        subject);
-            }
-        }
-    }
-
-    private ResourceOperation getResourceOperation(final UnmatchingRule rule) {
-        switch (rule) {
-            case ASSIGN:
-            case PROVISION:
-                return ResourceOperation.CREATE;
-            default:
-                return ResourceOperation.NONE;
-        }
-    }
-
-    private ResourceOperation getResourceOperation(final MatchingRule rule) {
-        switch (rule) {
-            case UPDATE:
-                return ResourceOperation.UPDATE;
-            case DEPROVISION:
-            case UNASSIGN:
-                return ResourceOperation.DELETE;
-            default:
-                return ResourceOperation.NONE;
-        }
-    }
-
-    protected Subject<?, ?, ?> update(final Subject<?, ?, ?> sbj, final Boolean enabled) {
-
-        final Set<MembershipMod> membsToAdd = new HashSet<>();
-        final Set<String> vattrToBeRemoved = new HashSet<>();
-        final Set<String> membVattrToBeRemoved = new HashSet<>();
-        final Set<AttrMod> vattrToBeUpdated = new HashSet<>();
-
-        // Search for all mapped vattrs
-        final Mapping<?> umapping = getMapping();
-        for (MappingItem mappingItem : umapping.getItems()) {
-            if (mappingItem.getIntMappingType() == IntMappingType.UserVirtualSchema) {
-                vattrToBeRemoved.add(mappingItem.getIntAttrName());
-            } else if (mappingItem.getIntMappingType() == IntMappingType.MembershipVirtualSchema) {
-                membVattrToBeRemoved.add(mappingItem.getIntAttrName());
-            }
-        }
-
-        // Search for all user's vattrs and:
-        // 1. add mapped vattrs not owned by the user to the set of vattrs to be removed
-        // 2. add all vattrs owned by the user to the set of vattrs to be update
-        for (VirAttr vattr : sbj.getVirAttrs()) {
-            vattrToBeRemoved.remove(vattr.getSchema().getKey());
-            final AttrMod mod = new AttrMod();
-            mod.setSchema(vattr.getSchema().getKey());
-            mod.getValuesToBeAdded().addAll(vattr.getValues());
-            vattrToBeUpdated.add(mod);
-        }
-
-        final boolean changepwd;
-
-        if (sbj instanceof User) {
-            changepwd = true;
-
-            // Search for memberships
-            for (Membership membership : User.class.cast(sbj).getMemberships()) {
-                final MembershipMod membershipMod = new MembershipMod();
-                membershipMod.setKey(membership.getKey());
-                membershipMod.setRole(membership.getRole().getKey());
-
-                for (VirAttr vattr : membership.getVirAttrs()) {
-                    membVattrToBeRemoved.remove(vattr.getSchema().getKey());
-                    final AttrMod mod = new AttrMod();
-                    mod.setSchema(vattr.getSchema().getKey());
-                    mod.getValuesToBeAdded().addAll(vattr.getValues());
-                    membershipMod.getVirAttrsToUpdate().add(mod);
-                }
-
-                membsToAdd.add(membershipMod);
-            }
-
-            if (!membsToAdd.isEmpty()) {
-                membsToAdd.iterator().next().getVirAttrsToRemove().addAll(membVattrToBeRemoved);
-            }
-        } else {
-            changepwd = false;
-        }
-
-        final List<String> noPropResources = new ArrayList<>(sbj.getResourceNames());
-        noPropResources.remove(profile.getTask().getResource().getKey());
-
-        final PropagationByResource propByRes = new PropagationByResource();
-        propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
-
-        taskExecutor.execute(propagationManager.getUpdateTaskIds(
-                sbj, null, changepwd, enabled, vattrToBeRemoved, vattrToBeUpdated, propByRes, noPropResources,
-                membsToAdd));
-
-        return userDAO.authFetch(sbj.getKey());
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSubjectSyncResultHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSubjectSyncResultHandler.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSubjectSyncResultHandler.java
deleted file mode 100644
index 3c49eee..0000000
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSubjectSyncResultHandler.java
+++ /dev/null
@@ -1,624 +0,0 @@
-/*
- * 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.server.provisioning.java.sync;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import org.apache.commons.lang3.exception.ExceptionUtils;
-import org.apache.syncope.common.lib.mod.AbstractSubjectMod;
-import org.apache.syncope.common.lib.to.AbstractSubjectTO;
-import org.apache.syncope.common.lib.types.AuditElements;
-import org.apache.syncope.common.lib.types.AuditElements.Result;
-import org.apache.syncope.common.lib.types.ResourceOperation;
-import org.apache.syncope.server.persistence.api.dao.NotFoundException;
-import org.apache.syncope.server.persistence.api.dao.UserDAO;
-import org.apache.syncope.server.persistence.api.entity.AttributableUtil;
-import org.apache.syncope.server.persistence.api.entity.task.SyncTask;
-import org.apache.syncope.server.provisioning.api.AttributableTransformer;
-import org.apache.syncope.server.provisioning.api.propagation.PropagationException;
-import org.apache.syncope.server.provisioning.api.sync.SyncActions;
-import org.apache.syncope.server.provisioning.api.sync.ProvisioningResult;
-import org.apache.syncope.server.misc.security.UnauthorizedRoleException;
-import org.identityconnectors.framework.common.objects.SyncDelta;
-import org.identityconnectors.framework.common.objects.SyncDeltaType;
-import org.identityconnectors.framework.common.objects.SyncResultsHandler;
-import org.quartz.JobExecutionException;
-import org.springframework.beans.factory.annotation.Autowired;
-
-public abstract class AbstractSubjectSyncResultHandler extends AbstractSyncopeResultHandler<SyncTask, SyncActions>
-        implements SyncResultsHandler {
-
-    @Autowired
-    protected SyncUtilities syncUtilities;
-
-    @Autowired
-    protected AttributableTransformer attrTransformer;
-
-    protected abstract AttributableUtil getAttributableUtil();
-
-    protected abstract String getName(AbstractSubjectTO subjectTO);
-
-    protected abstract AbstractSubjectTO getSubjectTO(long key);
-
-    protected abstract AbstractSubjectMod getSubjectMod(AbstractSubjectTO subjectTO, SyncDelta delta);
-
-    protected abstract AbstractSubjectTO create(AbstractSubjectTO subjectTO, SyncDelta _delta, ProvisioningResult result);
-
-    protected abstract AbstractSubjectTO link(AbstractSubjectTO before, ProvisioningResult result, boolean unlink);
-
-    protected abstract AbstractSubjectTO update(AbstractSubjectTO before, AbstractSubjectMod subjectMod,
-            SyncDelta delta, ProvisioningResult result);
-
-    protected abstract void deprovision(Long key, boolean unlink);
-
-    protected abstract void delete(Long key);
-
-    @Override
-    public boolean handle(final SyncDelta delta) {
-        try {
-            doHandle(delta, profile.getResults());
-            return true;
-        } catch (JobExecutionException e) {
-            LOG.error("Synchronization failed", e);
-            return false;
-        }
-    }
-
-    protected List<ProvisioningResult> assign(final SyncDelta delta, final AttributableUtil attrUtil)
-            throws JobExecutionException {
-        if (!profile.getTask().isPerformCreate()) {
-            LOG.debug("SyncTask not configured for create");
-            return Collections.<ProvisioningResult>emptyList();
-        }
-
-        final AbstractSubjectTO subjectTO =
-                connObjectUtil.getSubjectTO(delta.getObject(), profile.getTask(), attrUtil);
-
-        subjectTO.getResources().add(profile.getTask().getResource().getKey());
-
-        final ProvisioningResult result = new ProvisioningResult();
-        result.setOperation(ResourceOperation.CREATE);
-        result.setSubjectType(attrUtil.getType());
-        result.setStatus(ProvisioningResult.Status.SUCCESS);
-
-        // Attributable transformation (if configured)
-        AbstractSubjectTO transformed = attrTransformer.transform(subjectTO);
-        LOG.debug("Transformed: {}", transformed);
-
-        result.setName(getName(transformed));
-
-        if (profile.isDryRun()) {
-            result.setId(0L);
-        } else {
-            SyncDelta _delta = delta;
-            for (SyncActions action : profile.getActions()) {
-                _delta = action.beforeAssign(this.getProfile(), _delta, transformed);
-            }
-
-            create(transformed, _delta, attrUtil, "assign", result);
-        }
-
-        return Collections.singletonList(result);
-    }
-
-    protected List<ProvisioningResult> create(final SyncDelta delta, final AttributableUtil attrUtil)
-            throws JobExecutionException {
-
-        if (!profile.getTask().isPerformCreate()) {
-            LOG.debug("SyncTask not configured for create");
-            return Collections.<ProvisioningResult>emptyList();
-        }
-
-        final AbstractSubjectTO subjectTO =
-                connObjectUtil.getSubjectTO(delta.getObject(), profile.getTask(), attrUtil);
-
-        // Attributable transformation (if configured)
-        AbstractSubjectTO transformed = attrTransformer.transform(subjectTO);
-        LOG.debug("Transformed: {}", transformed);
-
-        final ProvisioningResult result = new ProvisioningResult();
-        result.setOperation(ResourceOperation.CREATE);
-        result.setSubjectType(attrUtil.getType());
-        result.setStatus(ProvisioningResult.Status.SUCCESS);
-
-        result.setName(getName(transformed));
-
-        if (profile.isDryRun()) {
-            result.setId(0L);
-        } else {
-            SyncDelta _delta = delta;
-            for (SyncActions action : profile.getActions()) {
-                _delta = action.beforeProvision(this.getProfile(), _delta, transformed);
-            }
-
-            create(transformed, _delta, attrUtil, "provision", result);
-        }
-
-        return Collections.<ProvisioningResult>singletonList(result);
-    }
-
-    private void create(
-            final AbstractSubjectTO subjectTO,
-            final SyncDelta delta,
-            final AttributableUtil attrUtil,
-            final String operation,
-            final ProvisioningResult result)
-            throws JobExecutionException {
-
-        Object output;
-        Result resultStatus;
-
-        try {
-            AbstractSubjectTO actual = create(subjectTO, delta, result);
-            result.setName(getName(actual));
-            output = actual;
-            resultStatus = Result.SUCCESS;
-
-            for (SyncActions action : profile.getActions()) {
-                action.after(this.getProfile(), delta, actual, result);
-            }
-        } catch (PropagationException e) {
-            // A propagation failure doesn't imply a synchronization failure.
-            // The propagation exception status will be reported into the propagation task execution.
-            LOG.error("Could not propagate {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
-            output = e;
-            resultStatus = Result.FAILURE;
-        } catch (Exception e) {
-            result.setStatus(ProvisioningResult.Status.FAILURE);
-            result.setMessage(ExceptionUtils.getRootCauseMessage(e));
-            LOG.error("Could not create {} {} ", attrUtil.getType(), delta.getUid().getUidValue(), e);
-            output = e;
-            resultStatus = Result.FAILURE;
-        }
-
-        audit(operation, resultStatus, null, output, delta);
-    }
-
-    protected List<ProvisioningResult> update(SyncDelta delta, final List<Long> subjects,
-            final AttributableUtil attrUtil)
-            throws JobExecutionException {
-
-        if (!profile.getTask().isPerformUpdate()) {
-            LOG.debug("SyncTask not configured for update");
-            return Collections.<ProvisioningResult>emptyList();
-        }
-
-        LOG.debug("About to update {}", subjects);
-
-        List<ProvisioningResult> updResults = new ArrayList<>();
-
-        for (Long id : subjects) {
-            LOG.debug("About to update {}", id);
-
-            Object output;
-            AbstractSubjectTO before = null;
-            Result resultStatus;
-
-            final ProvisioningResult result = new ProvisioningResult();
-            result.setOperation(ResourceOperation.UPDATE);
-            result.setSubjectType(attrUtil.getType());
-            result.setStatus(ProvisioningResult.Status.SUCCESS);
-            result.setId(id);
-
-            before = getSubjectTO(id);
-
-            if (before == null) {
-                result.setStatus(ProvisioningResult.Status.FAILURE);
-                result.setMessage(String.format("Subject '%s(%d)' not found", attrUtil.getType().name(), id));
-            } else {
-                result.setName(getName(before));
-            }
-
-            if (!profile.isDryRun()) {
-                if (before == null) {
-                    resultStatus = Result.FAILURE;
-                    output = null;
-                } else {
-                    try {
-                        final AbstractSubjectMod attributableMod = getSubjectMod(before, delta);
-
-                        // Attribute value transformation (if configured)
-                        final AbstractSubjectMod actual = attrTransformer.transform(attributableMod);
-                        LOG.debug("Transformed: {}", actual);
-
-                        for (SyncActions action : profile.getActions()) {
-                            delta = action.beforeUpdate(this.getProfile(), delta, before, attributableMod);
-                        }
-
-                        final AbstractSubjectTO updated = update(before, attributableMod, delta, result);
-
-                        for (SyncActions action : profile.getActions()) {
-                            action.after(this.getProfile(), delta, updated, result);
-                        }
-
-                        output = updated;
-                        resultStatus = Result.SUCCESS;
-                        result.setName(getName(updated));
-                        LOG.debug("{} {} successfully updated", attrUtil.getType(), id);
-                    } catch (PropagationException e) {
-                        // A propagation failure doesn't imply a synchronization failure.
-                        // The propagation exception status will be reported into the propagation task execution.
-                        LOG.error("Could not propagate {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
-                        output = e;
-                        resultStatus = Result.FAILURE;
-                    } catch (Exception e) {
-                        result.setStatus(ProvisioningResult.Status.FAILURE);
-                        result.setMessage(ExceptionUtils.getRootCauseMessage(e));
-                        LOG.error("Could not update {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
-                        output = e;
-                        resultStatus = Result.FAILURE;
-                    }
-                }
-                audit("update", resultStatus, before, output, delta);
-            }
-            updResults.add(result);
-        }
-        return updResults;
-    }
-
-    protected List<ProvisioningResult> deprovision(
-            SyncDelta delta,
-            final List<Long> subjects,
-            final AttributableUtil attrUtil,
-            final boolean unlink)
-            throws JobExecutionException {
-
-        if (!profile.getTask().isPerformUpdate()) {
-            LOG.debug("SyncTask not configured for update");
-            return Collections.<ProvisioningResult>emptyList();
-        }
-
-        LOG.debug("About to update {}", subjects);
-
-        final List<ProvisioningResult> updResults = new ArrayList<>();
-
-        for (Long id : subjects) {
-            LOG.debug("About to unassign resource {}", id);
-
-            Object output;
-            Result resultStatus;
-
-            final ProvisioningResult result = new ProvisioningResult();
-            result.setOperation(ResourceOperation.DELETE);
-            result.setSubjectType(attrUtil.getType());
-            result.setStatus(ProvisioningResult.Status.SUCCESS);
-            result.setId(id);
-
-            final AbstractSubjectTO before = getSubjectTO(id);
-
-            if (before == null) {
-                result.setStatus(ProvisioningResult.Status.FAILURE);
-                result.setMessage(String.format("Subject '%s(%d)' not found", attrUtil.getType().name(), id));
-            }
-
-            if (!profile.isDryRun()) {
-                if (before == null) {
-                    resultStatus = Result.FAILURE;
-                    output = null;
-                } else {
-                    result.setName(getName(before));
-
-                    try {
-                        if (unlink) {
-                            for (SyncActions action : profile.getActions()) {
-                                action.beforeUnassign(this.getProfile(), delta, before);
-                            }
-                        } else {
-                            for (SyncActions action : profile.getActions()) {
-                                action.beforeDeprovision(this.getProfile(), delta, before);
-                            }
-                        }
-
-                        deprovision(id, unlink);
-                        output = getSubjectTO(id);
-
-                        for (SyncActions action : profile.getActions()) {
-                            action.after(this.getProfile(), delta, AbstractSubjectTO.class.cast(output), result);
-                        }
-
-                        resultStatus = Result.SUCCESS;
-                        LOG.debug("{} {} successfully updated", attrUtil.getType(), id);
-                    } catch (PropagationException e) {
-                        // A propagation failure doesn't imply a synchronization failure.
-                        // The propagation exception status will be reported into the propagation task execution.
-                        LOG.error("Could not propagate {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
-                        output = e;
-                        resultStatus = Result.FAILURE;
-                    } catch (Exception e) {
-                        result.setStatus(ProvisioningResult.Status.FAILURE);
-                        result.setMessage(ExceptionUtils.getRootCauseMessage(e));
-                        LOG.error("Could not update {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
-                        output = e;
-                        resultStatus = Result.FAILURE;
-                    }
-                }
-                audit(unlink ? "unassign" : "deprovision", resultStatus, before, output, delta);
-            }
-            updResults.add(result);
-        }
-
-        return updResults;
-    }
-
-    protected List<ProvisioningResult> link(
-            SyncDelta delta,
-            final List<Long> subjects,
-            final AttributableUtil attrUtil,
-            final boolean unlink)
-            throws JobExecutionException {
-
-        if (!profile.getTask().isPerformUpdate()) {
-            LOG.debug("SyncTask not configured for update");
-            return Collections.<ProvisioningResult>emptyList();
-        }
-
-        LOG.debug("About to update {}", subjects);
-
-        final List<ProvisioningResult> updResults = new ArrayList<>();
-
-        for (Long id : subjects) {
-            LOG.debug("About to unassign resource {}", id);
-
-            Object output;
-            Result resultStatus;
-
-            final ProvisioningResult result = new ProvisioningResult();
-            result.setOperation(ResourceOperation.NONE);
-            result.setSubjectType(attrUtil.getType());
-            result.setStatus(ProvisioningResult.Status.SUCCESS);
-            result.setId(id);
-
-            final AbstractSubjectTO before = getSubjectTO(id);
-
-            if (before == null) {
-                result.setStatus(ProvisioningResult.Status.FAILURE);
-                result.setMessage(String.format("Subject '%s(%d)' not found", attrUtil.getType().name(), id));
-            }
-
-            if (!profile.isDryRun()) {
-                if (before == null) {
-                    resultStatus = Result.FAILURE;
-                    output = null;
-                } else {
-                    result.setName(getName(before));
-
-                    try {
-                        if (unlink) {
-                            for (SyncActions action : profile.getActions()) {
-                                action.beforeUnlink(this.getProfile(), delta, before);
-                            }
-                        } else {
-                            for (SyncActions action : profile.getActions()) {
-                                action.beforeLink(this.getProfile(), delta, before);
-                            }
-                        }
-
-                        output = link(before, result, unlink);
-
-                        for (SyncActions action : profile.getActions()) {
-                            action.after(this.getProfile(), delta, AbstractSubjectTO.class.cast(output), result);
-                        }
-
-                        resultStatus = Result.SUCCESS;
-                        LOG.debug("{} {} successfully updated", attrUtil.getType(), id);
-                    } catch (PropagationException e) {
-                        // A propagation failure doesn't imply a synchronization failure.
-                        // The propagation exception status will be reported into the propagation task execution.
-                        LOG.error("Could not propagate {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
-                        output = e;
-                        resultStatus = Result.FAILURE;
-                    } catch (Exception e) {
-                        result.setStatus(ProvisioningResult.Status.FAILURE);
-                        result.setMessage(ExceptionUtils.getRootCauseMessage(e));
-                        LOG.error("Could not update {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
-                        output = e;
-                        resultStatus = Result.FAILURE;
-                    }
-                }
-                audit(unlink ? "unlink" : "link", resultStatus, before, output, delta);
-            }
-            updResults.add(result);
-        }
-
-        return updResults;
-    }
-
-    protected List<ProvisioningResult> delete(
-            SyncDelta delta, final List<Long> subjects, final AttributableUtil attrUtil)
-            throws JobExecutionException {
-
-        if (!profile.getTask().isPerformDelete()) {
-            LOG.debug("SyncTask not configured for delete");
-            return Collections.<ProvisioningResult>emptyList();
-        }
-
-        LOG.debug("About to delete {}", subjects);
-
-        List<ProvisioningResult> delResults = new ArrayList<>();
-
-        for (Long id : subjects) {
-            Object output;
-            Result resultStatus = Result.FAILURE;
-
-            AbstractSubjectTO before = null;
-            final ProvisioningResult result = new ProvisioningResult();
-
-            try {
-                before = getSubjectTO(id);
-
-                result.setId(id);
-                result.setName(getName(before));
-                result.setOperation(ResourceOperation.DELETE);
-                result.setSubjectType(attrUtil.getType());
-                result.setStatus(ProvisioningResult.Status.SUCCESS);
-
-                if (!profile.isDryRun()) {
-                    for (SyncActions action : profile.getActions()) {
-                        delta = action.beforeDelete(this.getProfile(), delta, before);
-                    }
-
-                    try {
-                        delete(id);
-                        output = null;
-                        resultStatus = Result.SUCCESS;
-                    } catch (Exception e) {
-                        result.setStatus(ProvisioningResult.Status.FAILURE);
-                        result.setMessage(ExceptionUtils.getRootCauseMessage(e));
-                        LOG.error("Could not delete {} {}", attrUtil.getType(), id, e);
-                        output = e;
-                    }
-
-                    for (SyncActions action : profile.getActions()) {
-                        action.after(this.getProfile(), delta, before, result);
-                    }
-
-                    audit("delete", resultStatus, before, output, delta);
-                }
-
-                delResults.add(result);
-
-            } catch (NotFoundException e) {
-                LOG.error("Could not find {} {}", attrUtil.getType(), id, e);
-            } catch (UnauthorizedRoleException e) {
-                LOG.error("Not allowed to read {} {}", attrUtil.getType(), id, e);
-            } catch (Exception e) {
-                LOG.error("Could not delete {} {}", attrUtil.getType(), id, e);
-            }
-        }
-
-        return delResults;
-    }
-
-    /**
-     * Look into SyncDelta and take necessary profile.getActions() (create / update / delete) on user(s)/role(s).
-     *
-     * @param delta returned by the underlying profile.getConnector()
-     * @throws JobExecutionException in case of synchronization failure.
-     */
-    protected final void doHandle(final SyncDelta delta, final Collection<ProvisioningResult> syncResults)
-            throws JobExecutionException {
-
-        final AttributableUtil attrUtil = getAttributableUtil();
-
-        LOG.debug("Process {} for {} as {}",
-                delta.getDeltaType(), delta.getUid().getUidValue(), delta.getObject().getObjectClass());
-
-        final String uid = delta.getPreviousUid() == null
-                ? delta.getUid().getUidValue()
-                : delta.getPreviousUid().getUidValue();
-
-        try {
-            List<Long> subjectIds = syncUtilities.findExisting(
-                    uid, delta.getObject(), profile.getTask().getResource(), attrUtil);
-
-            if (subjectIds.size() > 1) {
-                switch (profile.getResAct()) {
-                    case IGNORE:
-                        throw new IllegalStateException("More than one match " + subjectIds);
-
-                    case FIRSTMATCH:
-                        subjectIds = subjectIds.subList(0, 1);
-                        break;
-
-                    case LASTMATCH:
-                        subjectIds = subjectIds.subList(subjectIds.size() - 1, subjectIds.size());
-                        break;
-
-                    default:
-                    // keep subjectIds as is
-                }
-            }
-
-            if (SyncDeltaType.CREATE_OR_UPDATE == delta.getDeltaType()) {
-                if (subjectIds.isEmpty()) {
-                    switch (profile.getTask().getUnmatchingRule()) {
-                        case ASSIGN:
-                            profile.getResults().addAll(assign(delta, attrUtil));
-                            break;
-                        case PROVISION:
-                            profile.getResults().addAll(create(delta, attrUtil));
-                            break;
-                        default:
-                        // do nothing
-                    }
-                } else {
-                    switch (profile.getTask().getMatchingRule()) {
-                        case UPDATE:
-                            profile.getResults().addAll(update(delta, subjectIds, attrUtil));
-                            break;
-                        case DEPROVISION:
-                            profile.getResults().addAll(deprovision(delta, subjectIds, attrUtil, false));
-                            break;
-                        case UNASSIGN:
-                            profile.getResults().addAll(deprovision(delta, subjectIds, attrUtil, true));
-                            break;
-                        case LINK:
-                            profile.getResults().addAll(link(delta, subjectIds, attrUtil, false));
-                            break;
-                        case UNLINK:
-                            profile.getResults().addAll(link(delta, subjectIds, attrUtil, true));
-                            break;
-                        default:
-                        // do nothing
-                    }
-                }
-            } else if (SyncDeltaType.DELETE == delta.getDeltaType()) {
-                if (subjectIds.isEmpty()) {
-                    LOG.debug("No match found for deletion");
-                } else {
-                    profile.getResults().addAll(delete(delta, subjectIds, attrUtil));
-                }
-            }
-        } catch (IllegalStateException e) {
-            LOG.warn(e.getMessage());
-        } catch (IllegalArgumentException e) {
-            LOG.warn(e.getMessage());
-        }
-    }
-
-    private void audit(
-            final String event,
-            final Result result,
-            final Object before,
-            final Object output,
-            final Object... input) {
-
-        notificationManager.createTasks(
-                AuditElements.EventCategoryType.SYNCHRONIZATION,
-                getAttributableUtil().getType().name().toLowerCase(),
-                profile.getTask().getResource().getKey(),
-                event,
-                result,
-                before,
-                output,
-                input);
-
-        auditManager.audit(
-                AuditElements.EventCategoryType.SYNCHRONIZATION,
-                getAttributableUtil().getType().name().toLowerCase(),
-                profile.getTask().getResource().getKey(),
-                event,
-                result,
-                before,
-                output,
-                input);
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSyncResultHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSyncResultHandler.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSyncResultHandler.java
new file mode 100644
index 0000000..83377f3
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSyncResultHandler.java
@@ -0,0 +1,617 @@
+/*
+ * 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.server.provisioning.java.sync;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.apache.syncope.common.lib.mod.AbstractSubjectMod;
+import org.apache.syncope.common.lib.to.AbstractSubjectTO;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.AuditElements.Result;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.server.persistence.api.dao.NotFoundException;
+import org.apache.syncope.server.persistence.api.entity.AttributableUtil;
+import org.apache.syncope.server.persistence.api.entity.task.SyncTask;
+import org.apache.syncope.server.provisioning.api.AttributableTransformer;
+import org.apache.syncope.server.provisioning.api.propagation.PropagationException;
+import org.apache.syncope.server.provisioning.api.sync.SyncActions;
+import org.apache.syncope.server.provisioning.api.sync.ProvisioningResult;
+import org.apache.syncope.server.misc.security.UnauthorizedRoleException;
+import org.apache.syncope.server.provisioning.api.sync.SyncopeSyncResultHandler;
+import org.identityconnectors.framework.common.objects.SyncDelta;
+import org.identityconnectors.framework.common.objects.SyncDeltaType;
+import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public abstract class AbstractSyncResultHandler extends AbstractSyncopeResultHandler<SyncTask, SyncActions>
+        implements SyncopeSyncResultHandler {
+
+    @Autowired
+    protected SyncUtilities syncUtilities;
+
+    @Autowired
+    protected AttributableTransformer attrTransformer;
+
+    protected abstract AttributableUtil getAttributableUtil();
+
+    protected abstract String getName(AbstractSubjectTO subjectTO);
+
+    protected abstract AbstractSubjectTO getSubjectTO(long key);
+
+    protected abstract AbstractSubjectMod getSubjectMod(AbstractSubjectTO subjectTO, SyncDelta delta);
+
+    protected abstract AbstractSubjectTO create(AbstractSubjectTO subjectTO, SyncDelta _delta, ProvisioningResult result);
+
+    protected abstract AbstractSubjectTO link(AbstractSubjectTO before, ProvisioningResult result, boolean unlink);
+
+    protected abstract AbstractSubjectTO update(AbstractSubjectTO before, AbstractSubjectMod subjectMod,
+            SyncDelta delta, ProvisioningResult result);
+
+    protected abstract void deprovision(Long key, boolean unlink);
+
+    protected abstract void delete(Long key);
+
+    @Override
+    public boolean handle(final SyncDelta delta) {
+        try {
+            doHandle(delta);
+            return true;
+        } catch (JobExecutionException e) {
+            LOG.error("Synchronization failed", e);
+            return false;
+        }
+    }
+
+    protected List<ProvisioningResult> assign(final SyncDelta delta, final AttributableUtil attrUtil)
+            throws JobExecutionException {
+        if (!profile.getTask().isPerformCreate()) {
+            LOG.debug("SyncTask not configured for create");
+            return Collections.<ProvisioningResult>emptyList();
+        }
+
+        final AbstractSubjectTO subjectTO =
+                connObjectUtil.getSubjectTO(delta.getObject(), profile.getTask(), attrUtil);
+
+        subjectTO.getResources().add(profile.getTask().getResource().getKey());
+
+        final ProvisioningResult result = new ProvisioningResult();
+        result.setOperation(ResourceOperation.CREATE);
+        result.setSubjectType(attrUtil.getType());
+        result.setStatus(ProvisioningResult.Status.SUCCESS);
+
+        // Attributable transformation (if configured)
+        AbstractSubjectTO transformed = attrTransformer.transform(subjectTO);
+        LOG.debug("Transformed: {}", transformed);
+
+        result.setName(getName(transformed));
+
+        if (profile.isDryRun()) {
+            result.setId(0L);
+        } else {
+            SyncDelta _delta = delta;
+            for (SyncActions action : profile.getActions()) {
+                _delta = action.beforeAssign(this.getProfile(), _delta, transformed);
+            }
+
+            create(transformed, _delta, attrUtil, "assign", result);
+        }
+
+        return Collections.singletonList(result);
+    }
+
+    protected List<ProvisioningResult> create(final SyncDelta delta, final AttributableUtil attrUtil)
+            throws JobExecutionException {
+
+        if (!profile.getTask().isPerformCreate()) {
+            LOG.debug("SyncTask not configured for create");
+            return Collections.<ProvisioningResult>emptyList();
+        }
+
+        final AbstractSubjectTO subjectTO =
+                connObjectUtil.getSubjectTO(delta.getObject(), profile.getTask(), attrUtil);
+
+        // Attributable transformation (if configured)
+        AbstractSubjectTO transformed = attrTransformer.transform(subjectTO);
+        LOG.debug("Transformed: {}", transformed);
+
+        final ProvisioningResult result = new ProvisioningResult();
+        result.setOperation(ResourceOperation.CREATE);
+        result.setSubjectType(attrUtil.getType());
+        result.setStatus(ProvisioningResult.Status.SUCCESS);
+
+        result.setName(getName(transformed));
+
+        if (profile.isDryRun()) {
+            result.setId(0L);
+        } else {
+            SyncDelta _delta = delta;
+            for (SyncActions action : profile.getActions()) {
+                _delta = action.beforeProvision(this.getProfile(), _delta, transformed);
+            }
+
+            create(transformed, _delta, attrUtil, "provision", result);
+        }
+
+        return Collections.<ProvisioningResult>singletonList(result);
+    }
+
+    private void create(
+            final AbstractSubjectTO subjectTO,
+            final SyncDelta delta,
+            final AttributableUtil attrUtil,
+            final String operation,
+            final ProvisioningResult result)
+            throws JobExecutionException {
+
+        Object output;
+        Result resultStatus;
+
+        try {
+            AbstractSubjectTO actual = create(subjectTO, delta, result);
+            result.setName(getName(actual));
+            output = actual;
+            resultStatus = Result.SUCCESS;
+
+            for (SyncActions action : profile.getActions()) {
+                action.after(this.getProfile(), delta, actual, result);
+            }
+        } catch (PropagationException e) {
+            // A propagation failure doesn't imply a synchronization failure.
+            // The propagation exception status will be reported into the propagation task execution.
+            LOG.error("Could not propagate {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
+            output = e;
+            resultStatus = Result.FAILURE;
+        } catch (Exception e) {
+            result.setStatus(ProvisioningResult.Status.FAILURE);
+            result.setMessage(ExceptionUtils.getRootCauseMessage(e));
+            LOG.error("Could not create {} {} ", attrUtil.getType(), delta.getUid().getUidValue(), e);
+            output = e;
+            resultStatus = Result.FAILURE;
+        }
+
+        audit(operation, resultStatus, null, output, delta);
+    }
+
+    protected List<ProvisioningResult> update(SyncDelta delta, final List<Long> subjects,
+            final AttributableUtil attrUtil)
+            throws JobExecutionException {
+
+        if (!profile.getTask().isPerformUpdate()) {
+            LOG.debug("SyncTask not configured for update");
+            return Collections.<ProvisioningResult>emptyList();
+        }
+
+        LOG.debug("About to update {}", subjects);
+
+        List<ProvisioningResult> results = new ArrayList<>();
+
+        for (Long key : subjects) {
+            LOG.debug("About to update {}", key);
+
+            final ProvisioningResult result = new ProvisioningResult();
+            result.setOperation(ResourceOperation.UPDATE);
+            result.setSubjectType(attrUtil.getType());
+            result.setStatus(ProvisioningResult.Status.SUCCESS);
+            result.setId(key);
+
+            AbstractSubjectTO before = getSubjectTO(key);
+            if (before == null) {
+                result.setStatus(ProvisioningResult.Status.FAILURE);
+                result.setMessage(String.format("Subject '%s(%d)' not found", attrUtil.getType().name(), key));
+            } else {
+                result.setName(getName(before));
+            }
+
+            Result resultStatus;
+            Object output;
+            if (!profile.isDryRun()) {
+                if (before == null) {
+                    resultStatus = Result.FAILURE;
+                    output = null;
+                } else {
+                    try {
+                        final AbstractSubjectMod attributableMod = getSubjectMod(before, delta);
+
+                        // Attribute value transformation (if configured)
+                        final AbstractSubjectMod actual = attrTransformer.transform(attributableMod);
+                        LOG.debug("Transformed: {}", actual);
+
+                        for (SyncActions action : profile.getActions()) {
+                            delta = action.beforeUpdate(this.getProfile(), delta, before, attributableMod);
+                        }
+
+                        final AbstractSubjectTO updated = update(before, attributableMod, delta, result);
+
+                        for (SyncActions action : profile.getActions()) {
+                            action.after(this.getProfile(), delta, updated, result);
+                        }
+
+                        output = updated;
+                        resultStatus = Result.SUCCESS;
+                        result.setName(getName(updated));
+                        LOG.debug("{} {} successfully updated", attrUtil.getType(), key);
+                    } catch (PropagationException e) {
+                        // A propagation failure doesn't imply a synchronization failure.
+                        // The propagation exception status will be reported into the propagation task execution.
+                        LOG.error("Could not propagate {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
+                        output = e;
+                        resultStatus = Result.FAILURE;
+                    } catch (Exception e) {
+                        result.setStatus(ProvisioningResult.Status.FAILURE);
+                        result.setMessage(ExceptionUtils.getRootCauseMessage(e));
+                        LOG.error("Could not update {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
+                        output = e;
+                        resultStatus = Result.FAILURE;
+                    }
+                }
+                audit("update", resultStatus, before, output, delta);
+            }
+            results.add(result);
+        }
+        return results;
+    }
+
+    protected List<ProvisioningResult> deprovision(
+            SyncDelta delta,
+            final List<Long> subjects,
+            final AttributableUtil attrUtil,
+            final boolean unlink)
+            throws JobExecutionException {
+
+        if (!profile.getTask().isPerformUpdate()) {
+            LOG.debug("SyncTask not configured for update");
+            return Collections.<ProvisioningResult>emptyList();
+        }
+
+        LOG.debug("About to update {}", subjects);
+
+        final List<ProvisioningResult> updResults = new ArrayList<>();
+
+        for (Long id : subjects) {
+            LOG.debug("About to unassign resource {}", id);
+
+            Object output;
+            Result resultStatus;
+
+            final ProvisioningResult result = new ProvisioningResult();
+            result.setOperation(ResourceOperation.DELETE);
+            result.setSubjectType(attrUtil.getType());
+            result.setStatus(ProvisioningResult.Status.SUCCESS);
+            result.setId(id);
+
+            final AbstractSubjectTO before = getSubjectTO(id);
+
+            if (before == null) {
+                result.setStatus(ProvisioningResult.Status.FAILURE);
+                result.setMessage(String.format("Subject '%s(%d)' not found", attrUtil.getType().name(), id));
+            }
+
+            if (!profile.isDryRun()) {
+                if (before == null) {
+                    resultStatus = Result.FAILURE;
+                    output = null;
+                } else {
+                    result.setName(getName(before));
+
+                    try {
+                        if (unlink) {
+                            for (SyncActions action : profile.getActions()) {
+                                action.beforeUnassign(this.getProfile(), delta, before);
+                            }
+                        } else {
+                            for (SyncActions action : profile.getActions()) {
+                                action.beforeDeprovision(this.getProfile(), delta, before);
+                            }
+                        }
+
+                        deprovision(id, unlink);
+                        output = getSubjectTO(id);
+
+                        for (SyncActions action : profile.getActions()) {
+                            action.after(this.getProfile(), delta, AbstractSubjectTO.class.cast(output), result);
+                        }
+
+                        resultStatus = Result.SUCCESS;
+                        LOG.debug("{} {} successfully updated", attrUtil.getType(), id);
+                    } catch (PropagationException e) {
+                        // A propagation failure doesn't imply a synchronization failure.
+                        // The propagation exception status will be reported into the propagation task execution.
+                        LOG.error("Could not propagate {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
+                        output = e;
+                        resultStatus = Result.FAILURE;
+                    } catch (Exception e) {
+                        result.setStatus(ProvisioningResult.Status.FAILURE);
+                        result.setMessage(ExceptionUtils.getRootCauseMessage(e));
+                        LOG.error("Could not update {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
+                        output = e;
+                        resultStatus = Result.FAILURE;
+                    }
+                }
+                audit(unlink ? "unassign" : "deprovision", resultStatus, before, output, delta);
+            }
+            updResults.add(result);
+        }
+
+        return updResults;
+    }
+
+    protected List<ProvisioningResult> link(
+            SyncDelta delta,
+            final List<Long> subjects,
+            final AttributableUtil attrUtil,
+            final boolean unlink)
+            throws JobExecutionException {
+
+        if (!profile.getTask().isPerformUpdate()) {
+            LOG.debug("SyncTask not configured for update");
+            return Collections.<ProvisioningResult>emptyList();
+        }
+
+        LOG.debug("About to update {}", subjects);
+
+        final List<ProvisioningResult> updResults = new ArrayList<>();
+
+        for (Long id : subjects) {
+            LOG.debug("About to unassign resource {}", id);
+
+            Object output;
+            Result resultStatus;
+
+            final ProvisioningResult result = new ProvisioningResult();
+            result.setOperation(ResourceOperation.NONE);
+            result.setSubjectType(attrUtil.getType());
+            result.setStatus(ProvisioningResult.Status.SUCCESS);
+            result.setId(id);
+
+            final AbstractSubjectTO before = getSubjectTO(id);
+
+            if (before == null) {
+                result.setStatus(ProvisioningResult.Status.FAILURE);
+                result.setMessage(String.format("Subject '%s(%d)' not found", attrUtil.getType().name(), id));
+            }
+
+            if (!profile.isDryRun()) {
+                if (before == null) {
+                    resultStatus = Result.FAILURE;
+                    output = null;
+                } else {
+                    result.setName(getName(before));
+
+                    try {
+                        if (unlink) {
+                            for (SyncActions action : profile.getActions()) {
+                                action.beforeUnlink(this.getProfile(), delta, before);
+                            }
+                        } else {
+                            for (SyncActions action : profile.getActions()) {
+                                action.beforeLink(this.getProfile(), delta, before);
+                            }
+                        }
+
+                        output = link(before, result, unlink);
+
+                        for (SyncActions action : profile.getActions()) {
+                            action.after(this.getProfile(), delta, AbstractSubjectTO.class.cast(output), result);
+                        }
+
+                        resultStatus = Result.SUCCESS;
+                        LOG.debug("{} {} successfully updated", attrUtil.getType(), id);
+                    } catch (PropagationException e) {
+                        // A propagation failure doesn't imply a synchronization failure.
+                        // The propagation exception status will be reported into the propagation task execution.
+                        LOG.error("Could not propagate {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
+                        output = e;
+                        resultStatus = Result.FAILURE;
+                    } catch (Exception e) {
+                        result.setStatus(ProvisioningResult.Status.FAILURE);
+                        result.setMessage(ExceptionUtils.getRootCauseMessage(e));
+                        LOG.error("Could not update {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
+                        output = e;
+                        resultStatus = Result.FAILURE;
+                    }
+                }
+                audit(unlink ? "unlink" : "link", resultStatus, before, output, delta);
+            }
+            updResults.add(result);
+        }
+
+        return updResults;
+    }
+
+    protected List<ProvisioningResult> delete(
+            SyncDelta delta, final List<Long> subjects, final AttributableUtil attrUtil)
+            throws JobExecutionException {
+
+        if (!profile.getTask().isPerformDelete()) {
+            LOG.debug("SyncTask not configured for delete");
+            return Collections.<ProvisioningResult>emptyList();
+        }
+
+        LOG.debug("About to delete {}", subjects);
+
+        List<ProvisioningResult> delResults = new ArrayList<>();
+
+        for (Long id : subjects) {
+            Object output;
+            Result resultStatus = Result.FAILURE;
+
+            AbstractSubjectTO before = null;
+            final ProvisioningResult result = new ProvisioningResult();
+
+            try {
+                before = getSubjectTO(id);
+
+                result.setId(id);
+                result.setName(getName(before));
+                result.setOperation(ResourceOperation.DELETE);
+                result.setSubjectType(attrUtil.getType());
+                result.setStatus(ProvisioningResult.Status.SUCCESS);
+
+                if (!profile.isDryRun()) {
+                    for (SyncActions action : profile.getActions()) {
+                        delta = action.beforeDelete(this.getProfile(), delta, before);
+                    }
+
+                    try {
+                        delete(id);
+                        output = null;
+                        resultStatus = Result.SUCCESS;
+                    } catch (Exception e) {
+                        result.setStatus(ProvisioningResult.Status.FAILURE);
+                        result.setMessage(ExceptionUtils.getRootCauseMessage(e));
+                        LOG.error("Could not delete {} {}", attrUtil.getType(), id, e);
+                        output = e;
+                    }
+
+                    for (SyncActions action : profile.getActions()) {
+                        action.after(this.getProfile(), delta, before, result);
+                    }
+
+                    audit("delete", resultStatus, before, output, delta);
+                }
+
+                delResults.add(result);
+
+            } catch (NotFoundException e) {
+                LOG.error("Could not find {} {}", attrUtil.getType(), id, e);
+            } catch (UnauthorizedRoleException e) {
+                LOG.error("Not allowed to read {} {}", attrUtil.getType(), id, e);
+            } catch (Exception e) {
+                LOG.error("Could not delete {} {}", attrUtil.getType(), id, e);
+            }
+        }
+
+        return delResults;
+    }
+
+    /**
+     * Look into SyncDelta and take necessary profile.getActions() (create / update / delete) on user(s)/role(s).
+     *
+     * @param delta returned by the underlying profile.getConnector()
+     * @throws JobExecutionException in case of synchronization failure.
+     */
+    protected final void doHandle(final SyncDelta delta)
+            throws JobExecutionException {
+
+        final AttributableUtil attrUtil = getAttributableUtil();
+
+        LOG.debug("Process {} for {} as {}",
+                delta.getDeltaType(), delta.getUid().getUidValue(), delta.getObject().getObjectClass());
+
+        final String uid = delta.getPreviousUid() == null
+                ? delta.getUid().getUidValue()
+                : delta.getPreviousUid().getUidValue();
+
+        try {
+            List<Long> subjectKeys = syncUtilities.findExisting(
+                    uid, delta.getObject(), profile.getTask().getResource(), attrUtil);
+
+            if (subjectKeys.size() > 1) {
+                switch (profile.getResAct()) {
+                    case IGNORE:
+                        throw new IllegalStateException("More than one match " + subjectKeys);
+
+                    case FIRSTMATCH:
+                        subjectKeys = subjectKeys.subList(0, 1);
+                        break;
+
+                    case LASTMATCH:
+                        subjectKeys = subjectKeys.subList(subjectKeys.size() - 1, subjectKeys.size());
+                        break;
+
+                    default:
+                    // keep subjectIds as is
+                }
+            }
+
+            if (SyncDeltaType.CREATE_OR_UPDATE == delta.getDeltaType()) {
+                if (subjectKeys.isEmpty()) {
+                    switch (profile.getTask().getUnmatchingRule()) {
+                        case ASSIGN:
+                            profile.getResults().addAll(assign(delta, attrUtil));
+                            break;
+                        case PROVISION:
+                            profile.getResults().addAll(create(delta, attrUtil));
+                            break;
+                        default:
+                        // do nothing
+                    }
+                } else {
+                    switch (profile.getTask().getMatchingRule()) {
+                        case UPDATE:
+                            profile.getResults().addAll(update(delta, subjectKeys, attrUtil));
+                            break;
+                        case DEPROVISION:
+                            profile.getResults().addAll(deprovision(delta, subjectKeys, attrUtil, false));
+                            break;
+                        case UNASSIGN:
+                            profile.getResults().addAll(deprovision(delta, subjectKeys, attrUtil, true));
+                            break;
+                        case LINK:
+                            profile.getResults().addAll(link(delta, subjectKeys, attrUtil, false));
+                            break;
+                        case UNLINK:
+                            profile.getResults().addAll(link(delta, subjectKeys, attrUtil, true));
+                            break;
+                        default:
+                        // do nothing
+                    }
+                }
+            } else if (SyncDeltaType.DELETE == delta.getDeltaType()) {
+                if (subjectKeys.isEmpty()) {
+                    LOG.debug("No match found for deletion");
+                } else {
+                    profile.getResults().addAll(delete(delta, subjectKeys, attrUtil));
+                }
+            }
+        } catch (IllegalStateException | IllegalArgumentException e) {
+            LOG.warn(e.getMessage());
+        }
+    }
+
+    private void audit(
+            final String event,
+            final Result result,
+            final Object before,
+            final Object output,
+            final Object... input) {
+
+        notificationManager.createTasks(
+                AuditElements.EventCategoryType.SYNCHRONIZATION,
+                getAttributableUtil().getType().name().toLowerCase(),
+                profile.getTask().getResource().getKey(),
+                event,
+                result,
+                before,
+                output,
+                input);
+
+        auditManager.audit(
+                AuditElements.EventCategoryType.SYNCHRONIZATION,
+                getAttributableUtil().getType().name().toLowerCase(),
+                profile.getTask().getResource().getKey(),
+                event,
+                result,
+                before,
+                output,
+                input);
+    }
+}