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 [2/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...

Added: syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectSyncResultHandler.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectSyncResultHandler.java?rev=1605361&view=auto
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectSyncResultHandler.java (added)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectSyncResultHandler.java Wed Jun 25 12:05:42 2014
@@ -0,0 +1,708 @@
+/*
+ * 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 org.apache.syncope.core.sync.SyncUtilities;
+import static org.apache.syncope.core.sync.impl.AbstractSyncopeResultHandler.LOG;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.apache.syncope.common.mod.AbstractSubjectMod;
+import org.apache.syncope.common.to.AbstractSubjectTO;
+import org.apache.syncope.common.to.RoleTO;
+import org.apache.syncope.common.to.UserTO;
+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.SyncTask;
+import org.apache.syncope.core.persistence.dao.NotFoundException;
+import org.apache.syncope.core.persistence.dao.UserDAO;
+import org.apache.syncope.core.propagation.PropagationException;
+import org.apache.syncope.core.propagation.PropagationTaskExecutor;
+import org.apache.syncope.core.propagation.impl.PropagationManager;
+import org.apache.syncope.core.rest.controller.AbstractSubjectController;
+import org.apache.syncope.core.rest.controller.UnauthorizedRoleException;
+import org.apache.syncope.core.rest.data.AttributableTransformer;
+import org.apache.syncope.core.sync.SyncActions;
+import org.apache.syncope.core.sync.SyncResult;
+import org.apache.syncope.core.util.AttributableUtil;
+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 PropagationManager propagationManager;
+
+    @Autowired
+    protected PropagationTaskExecutor taskExecutor;
+
+    @Autowired
+    protected AttributableTransformer attrTransformer;
+
+    @Autowired
+    protected UserDAO userDAO;
+
+    protected abstract AttributableUtil getAttributableUtil();
+
+    protected abstract String getName(
+            final AbstractSubjectTO subjectTO);
+
+    protected abstract AbstractSubjectTO getSubjectTO(
+            final long id);
+
+    protected abstract AbstractSubjectMod getSubjectMod(
+            final AbstractSubjectTO subjectTO, final SyncDelta delta);
+
+    protected abstract AbstractSubjectTO create(
+            final AbstractSubjectTO subjectTO,
+            final SyncDelta _delta,
+            final SyncResult result);
+
+    protected abstract AbstractSubjectTO update(
+            final AbstractSubjectTO before,
+            final AbstractSubjectMod subjectMod,
+            final SyncDelta delta,
+            final SyncResult result)
+            throws Exception;
+
+    protected abstract void delete(final Long id);
+
+    @Override
+    public boolean handle(final SyncDelta delta) {
+        try {
+            if (profile.getResults() == null) {
+                profile.setResults(new ArrayList<SyncResult>());
+            }
+
+            doHandle(delta, profile.getResults());
+            return true;
+        } catch (JobExecutionException e) {
+            LOG.error("Synchronization failed", e);
+            return false;
+        }
+    }
+
+    protected List<SyncResult> assign(
+            final SyncDelta delta, final AttributableUtil attrUtil, final boolean dryRun)
+            throws JobExecutionException {
+
+        final AbstractSubjectTO subjectTO =
+                connObjectUtil.getSubjectTO(delta.getObject(), profile.getSyncTask(), attrUtil);
+
+        subjectTO.getResources().add(profile.getSyncTask().getResource().getName());
+
+        SyncDelta _delta = delta;
+        for (SyncActions action : profile.getActions()) {
+            _delta = action.beforeAssign(this.getProfile(), _delta, subjectTO);
+        }
+
+        return create(subjectTO, _delta, attrUtil, "assign", dryRun);
+    }
+
+    protected List<SyncResult> create(
+            final SyncDelta delta, final AttributableUtil attrUtil, final boolean dryRun)
+            throws JobExecutionException {
+
+        final AbstractSubjectTO subjectTO =
+                connObjectUtil.getSubjectTO(delta.getObject(), profile.getSyncTask(), attrUtil);
+
+        return create(subjectTO, delta, attrUtil, "provision", dryRun);
+    }
+
+    private List<SyncResult> create(
+            final AbstractSubjectTO subjectTO,
+            final SyncDelta delta,
+            final AttributableUtil attrUtil,
+            final String operation,
+            final boolean dryRun)
+            throws JobExecutionException {
+
+        if (!profile.getSyncTask().isPerformCreate()) {
+            LOG.debug("SyncTask not configured for create");
+            return Collections.<SyncResult>emptyList();
+        }
+
+        final SyncResult result = new SyncResult();
+        result.setOperation(ResourceOperation.CREATE);
+        result.setSubjectType(attrUtil.getType());
+        result.setStatus(SyncResult.Status.SUCCESS);
+
+        // Attributable transformation (if configured)
+        AbstractSubjectTO actual = attrTransformer.transform(subjectTO);
+        LOG.debug("Transformed: {}", actual);
+
+        result.setName(getName(actual));
+
+        if (dryRun) {
+            result.setId(0L);
+        } else {
+            SyncDelta _delta = delta;
+            for (SyncActions action : profile.getActions()) {
+                _delta = action.beforeCreate(this.getProfile(), _delta, subjectTO);
+            }
+
+            Object output;
+            Result resultStatus;
+
+            try {
+                actual = create(actual, _delta, result);
+                output = actual;
+                resultStatus = Result.SUCCESS;
+            } 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(SyncResult.Status.FAILURE);
+                result.setMessage(e.getMessage());
+                LOG.error("Could not create {} {} ", attrUtil.getType(), _delta.getUid().getUidValue(), e);
+                output = e;
+                resultStatus = Result.FAILURE;
+            }
+
+            for (SyncActions action : profile.getActions()) {
+                action.after(this.getProfile(), _delta, actual, result);
+            }
+
+            notificationManager.createTasks(
+                    AuditElements.EventCategoryType.SYNCHRONIZATION,
+                    AttributableType.USER.name().toLowerCase(),
+                    profile.getSyncTask().getResource().getName(),
+                    operation,
+                    resultStatus,
+                    null,
+                    output,
+                    _delta);
+
+            auditManager.audit(
+                    AuditElements.EventCategoryType.SYNCHRONIZATION,
+                    AttributableType.USER.name().toLowerCase(),
+                    profile.getSyncTask().getResource().getName(),
+                    operation,
+                    resultStatus,
+                    null,
+                    output,
+                    _delta);
+        }
+
+        return Collections.singletonList(result);
+    }
+
+    protected List<SyncResult> update(SyncDelta delta, final List<Long> subjects, final AttributableUtil attrUtil,
+            final boolean dryRun)
+            throws JobExecutionException {
+
+        if (!profile.getSyncTask().isPerformUpdate()) {
+            LOG.debug("SyncTask not configured for update");
+            return Collections.<SyncResult>emptyList();
+        }
+
+        LOG.debug("About to update {}", subjects);
+
+        List<SyncResult> updResults = new ArrayList<SyncResult>();
+
+        for (Long id : subjects) {
+            LOG.debug("About to update {}", id);
+
+            Object output;
+            AbstractSubjectTO before = null;
+            Result resultStatus;
+
+            final SyncResult result = new SyncResult();
+            result.setOperation(ResourceOperation.UPDATE);
+            result.setSubjectType(attrUtil.getType());
+            result.setStatus(SyncResult.Status.SUCCESS);
+            result.setId(id);
+
+            try {
+                before = userDataBinder.getUserTO(id);
+
+                if (dryRun) {
+                    output = before;
+                } else {
+
+                    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;
+            } 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(SyncResult.Status.FAILURE);
+                result.setMessage(e.getMessage());
+                LOG.error("Could not update {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
+                output = e;
+                resultStatus = Result.FAILURE;
+            }
+            updResults.add(result);
+
+            if (!dryRun) {
+                notificationManager.createTasks(
+                        AuditElements.EventCategoryType.SYNCHRONIZATION,
+                        attrUtil.getType().name().toLowerCase(),
+                        profile.getSyncTask().getResource().getName(),
+                        "update",
+                        resultStatus,
+                        before,
+                        output,
+                        delta);
+
+                auditManager.audit(
+                        AuditElements.EventCategoryType.SYNCHRONIZATION,
+                        attrUtil.getType().name().toLowerCase(),
+                        profile.getSyncTask().getResource().getName(),
+                        "update",
+                        resultStatus,
+                        before,
+                        output,
+                        delta);
+            }
+
+            LOG.debug("{} {} successfully updated", attrUtil.getType(), id);
+        }
+
+        return updResults;
+    }
+
+    protected List<SyncResult> deprovision(
+            SyncDelta delta,
+            final List<Long> subjects,
+            final AttributableUtil attrUtil,
+            final boolean unlink,
+            final boolean dryRun)
+            throws JobExecutionException {
+
+        if (!profile.getSyncTask().isPerformUpdate()) {
+            LOG.debug("SyncTask not configured for update");
+            return Collections.<SyncResult>emptyList();
+        }
+
+        LOG.debug("About to update {}", subjects);
+
+        final List<SyncResult> updResults = new ArrayList<SyncResult>();
+
+        final AbstractSubjectController<?, ?> controller;
+        if (AttributableType.USER == attrUtil.getType()) {
+            controller = userController;
+        } else {
+            controller = roleController;
+        }
+
+        for (Long id : subjects) {
+            LOG.debug("About to unassign resource {}", id);
+
+            Object output;
+            Result resultStatus;
+
+            final SyncResult result = new SyncResult();
+            result.setOperation(ResourceOperation.DELETE);
+            result.setSubjectType(attrUtil.getType());
+            result.setStatus(SyncResult.Status.SUCCESS);
+            result.setId(id);
+
+            final AbstractSubjectTO before = controller.read(id);
+            result.setName(before instanceof UserTO ? UserTO.class.cast(before).getUsername()
+                    : before instanceof RoleTO ? RoleTO.class.cast(before).getName() : null);
+
+            try {
+                if (!dryRun) {
+                    if (unlink) {
+                        for (SyncActions action : profile.getActions()) {
+                            action.beforeUnassign(this.getProfile(), delta, before);
+                        }
+                        controller.unlink(
+                                id, Collections.<String>singleton(profile.getSyncTask().getResource().getName()));
+                    } else {
+                        for (SyncActions action : profile.getActions()) {
+                            action.beforeDeprovision(this.getProfile(), delta, before);
+                        }
+                    }
+
+                    controller.deprovision(
+                            id, Collections.<String>singleton(profile.getSyncTask().getResource().getName()));
+
+                    output = controller.read(id);
+                    for (SyncActions action : profile.getActions()) {
+                        action.after(this.getProfile(), delta, AbstractSubjectTO.class.cast(output), result);
+                    }
+                } else {
+                    output = before;
+                }
+
+                resultStatus = Result.SUCCESS;
+            } 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(SyncResult.Status.FAILURE);
+                result.setMessage(e.getMessage());
+                LOG.error("Could not update {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
+                output = e;
+                resultStatus = Result.FAILURE;
+            }
+            updResults.add(result);
+
+            if (!dryRun) {
+                notificationManager.createTasks(
+                        AuditElements.EventCategoryType.SYNCHRONIZATION,
+                        attrUtil.getType().name().toLowerCase(),
+                        profile.getSyncTask().getResource().getName(),
+                        unlink ? "unassign" : "deprovision",
+                        resultStatus,
+                        before,
+                        output,
+                        delta);
+
+                auditManager.audit(
+                        AuditElements.EventCategoryType.SYNCHRONIZATION,
+                        attrUtil.getType().name().toLowerCase(),
+                        profile.getSyncTask().getResource().getName(),
+                        unlink ? "unassign" : "deprovision",
+                        resultStatus,
+                        before,
+                        output,
+                        delta);
+            }
+
+            LOG.debug("{} {} successfully updated", attrUtil.getType(), id);
+        }
+
+        return updResults;
+    }
+
+    protected List<SyncResult> link(
+            SyncDelta delta,
+            final List<Long> subjects,
+            final AttributableUtil attrUtil,
+            final boolean unlink,
+            final boolean dryRun)
+            throws JobExecutionException {
+
+        if (!profile.getSyncTask().isPerformUpdate()) {
+            LOG.debug("SyncTask not configured for update");
+            return Collections.<SyncResult>emptyList();
+        }
+
+        LOG.debug("About to update {}", subjects);
+
+        final List<SyncResult> updResults = new ArrayList<SyncResult>();
+
+        final AbstractSubjectController<?, ?> controller;
+        if (AttributableType.USER == attrUtil.getType()) {
+            controller = userController;
+        } else {
+            controller = roleController;
+        }
+
+        for (Long id : subjects) {
+            LOG.debug("About to unassign resource {}", id);
+
+            Object output;
+            Result resultStatus;
+
+            final SyncResult result = new SyncResult();
+            result.setOperation(ResourceOperation.NONE);
+            result.setSubjectType(attrUtil.getType());
+            result.setStatus(SyncResult.Status.SUCCESS);
+            result.setId(id);
+
+            final AbstractSubjectTO before = controller.read(id);
+            result.setName(before instanceof UserTO ? UserTO.class.cast(before).getUsername()
+                    : before instanceof RoleTO ? RoleTO.class.cast(before).getName() : null);
+
+            try {
+                if (!dryRun) {
+                    if (unlink) {
+                        for (SyncActions action : profile.getActions()) {
+                            action.beforeUnlink(this.getProfile(), delta, before);
+                        }
+                        controller.unlink(
+                                id, Collections.<String>singleton(profile.getSyncTask().getResource().getName()));
+                    } else {
+                        for (SyncActions action : profile.getActions()) {
+                            action.beforeLink(this.getProfile(), delta, before);
+                        }
+                        controller.link(
+                                id, Collections.<String>singleton(profile.getSyncTask().getResource().getName()));
+                    }
+
+                    output = controller.read(id);
+                    for (SyncActions action : profile.getActions()) {
+                        action.after(this.getProfile(), delta, AbstractSubjectTO.class.cast(output), result);
+                    }
+                } else {
+                    output = before;
+                }
+
+                resultStatus = Result.SUCCESS;
+            } 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(SyncResult.Status.FAILURE);
+                result.setMessage(e.getMessage());
+                LOG.error("Could not update {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
+                output = e;
+                resultStatus = Result.FAILURE;
+            }
+            updResults.add(result);
+
+            if (!dryRun) {
+                notificationManager.createTasks(
+                        AuditElements.EventCategoryType.SYNCHRONIZATION,
+                        attrUtil.getType().name().toLowerCase(),
+                        profile.getSyncTask().getResource().getName(),
+                        unlink ? "unlink" : "link",
+                        resultStatus,
+                        before,
+                        output,
+                        delta);
+
+                auditManager.audit(
+                        AuditElements.EventCategoryType.SYNCHRONIZATION,
+                        attrUtil.getType().name().toLowerCase(),
+                        profile.getSyncTask().getResource().getName(),
+                        unlink ? "unlink" : "link",
+                        resultStatus,
+                        before,
+                        output,
+                        delta);
+            }
+
+            LOG.debug("{} {} successfully updated", attrUtil.getType(), id);
+        }
+
+        return updResults;
+    }
+
+    protected List<SyncResult> delete(SyncDelta delta, final List<Long> subjects, final AttributableUtil attrUtil,
+            final boolean dryRun)
+            throws JobExecutionException {
+
+        if (!profile.getSyncTask().isPerformDelete()) {
+            LOG.debug("SyncTask not configured for delete");
+            return Collections.<SyncResult>emptyList();
+        }
+
+        LOG.debug("About to delete {}", subjects);
+
+        List<SyncResult> delResults = new ArrayList<SyncResult>();
+
+        for (Long id : subjects) {
+            Object output;
+            Result resultStatus = Result.FAILURE;
+
+            AbstractSubjectTO before = null;
+            final SyncResult result = new SyncResult();
+
+            try {
+                before = getSubjectTO(id);
+
+                result.setId(id);
+                if (before instanceof UserTO) {
+                    result.setName(((UserTO) before).getUsername());
+                }
+                if (before instanceof RoleTO) {
+                    result.setName(((RoleTO) before).getName());
+                }
+                result.setOperation(ResourceOperation.DELETE);
+                result.setSubjectType(attrUtil.getType());
+                result.setStatus(SyncResult.Status.SUCCESS);
+
+                if (!dryRun) {
+                    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(SyncResult.Status.FAILURE);
+                        result.setMessage(e.getMessage());
+                        LOG.error("Could not delete {} {}", attrUtil.getType(), id, e);
+                        output = e;
+                    }
+
+                    for (SyncActions action : profile.getActions()) {
+                        action.after(this.getProfile(), delta, before, result);
+                    }
+
+                    notificationManager.createTasks(
+                            AuditElements.EventCategoryType.SYNCHRONIZATION,
+                            attrUtil.getType().name().toLowerCase(),
+                            profile.getSyncTask().getResource().getName(),
+                            "delete",
+                            resultStatus,
+                            before,
+                            output,
+                            delta);
+
+                    auditManager.audit(
+                            AuditElements.EventCategoryType.SYNCHRONIZATION,
+                            attrUtil.getType().name().toLowerCase(),
+                            profile.getSyncTask().getResource().getName(),
+                            "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<SyncResult> 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.getSyncTask().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.getSyncTask().getUnmatchigRule()) {
+                        case ASSIGN:
+                            profile.getResults().addAll(assign(delta, attrUtil, profile.isDryRun()));
+                            break;
+                        case PROVISION:
+                            profile.getResults().addAll(create(delta, attrUtil, profile.isDryRun()));
+                            break;
+                        default:
+                        // do nothing
+                    }
+                } else {
+                    switch (profile.getSyncTask().getMatchigRule()) {
+                        case UPDATE:
+                            profile.getResults().addAll(update(delta, subjectIds, attrUtil, profile.isDryRun()));
+                            break;
+                        case DEPROVISION:
+                            profile.getResults().addAll(
+                                    deprovision(delta, subjectIds, attrUtil, false, profile.isDryRun()));
+                            break;
+                        case UNASSIGN:
+                            profile.getResults().addAll(
+                                    deprovision(delta, subjectIds, attrUtil, true, profile.isDryRun()));
+                            break;
+                        case LINK:
+                            profile.getResults().addAll(link(delta, subjectIds, attrUtil, false, profile.isDryRun()));
+                            break;
+                        case UNLINK:
+                            profile.getResults().addAll(link(delta, subjectIds, attrUtil, true, profile.isDryRun()));
+                            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, profile.isDryRun()));
+                }
+            }
+        } catch (IllegalStateException e) {
+            LOG.warn(e.getMessage());
+        }
+    }
+}

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSyncopeResultHandler.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSyncopeResultHandler.java?rev=1605361&r1=1605360&r2=1605361&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSyncopeResultHandler.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSyncopeResultHandler.java Wed Jun 25 12:05:42 2014
@@ -18,20 +18,16 @@
  */
 package org.apache.syncope.core.sync.impl;
 
-import java.util.Collection;
-import java.util.List;
-import org.apache.syncope.common.types.ConflictResolutionAction;
+import org.apache.syncope.core.sync.SyncProfile;
 import org.apache.syncope.core.audit.AuditManager;
 import org.apache.syncope.core.connid.ConnObjectUtil;
 import org.apache.syncope.core.notification.NotificationManager;
 import org.apache.syncope.core.persistence.beans.AbstractSyncTask;
-import org.apache.syncope.core.propagation.Connector;
 import org.apache.syncope.core.rest.controller.RoleController;
 import org.apache.syncope.core.rest.controller.UserController;
 import org.apache.syncope.core.rest.data.RoleDataBinder;
 import org.apache.syncope.core.rest.data.UserDataBinder;
 import org.apache.syncope.core.sync.AbstractSyncActions;
-import org.apache.syncope.core.sync.SyncResult;
 import org.apache.syncope.core.workflow.role.RoleWorkflowAdapter;
 import org.apache.syncope.core.workflow.user.UserWorkflowAdapter;
 import org.slf4j.Logger;
@@ -45,13 +41,12 @@ public abstract class AbstractSyncopeRes
      */
     protected static final Logger LOG = LoggerFactory.getLogger(AbstractSyncopeResultHandler.class);
 
-    
     @Autowired
     protected UserController userController;
 
     @Autowired
     protected RoleController roleController;
-    
+
     /**
      * User data binder.
      */
@@ -95,65 +90,16 @@ public abstract class AbstractSyncopeRes
     protected RoleWorkflowAdapter rwfAdapter;
 
     /**
-     * Syncing connector.
+     * Sync profile.
      */
-    protected Connector connector;
-
-    protected Collection<SyncResult> results;
-
-    protected boolean dryRun;
-
-    protected ConflictResolutionAction resAct;
-
-    protected List<A> actions;
-
-    protected T syncTask;
-
-    public List<A> getActions() {
-        return actions;
-    }
-
-    public void setActions(final List<A> actions) {
-        this.actions = actions;
-    }
-
-    public T getSyncTask() {
-        return syncTask;
-    }
-
-    public void setSyncTask(T syncTask) {
-        this.syncTask = syncTask;
-    }
-
-    public Connector getConnector() {
-        return connector;
-    }
-
-    public void setConnector(final Connector connector) {
-        this.connector = 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(final boolean dryRun) {
-        this.dryRun = dryRun;
-    }
+    protected SyncProfile<T, A> profile;
 
-    public ConflictResolutionAction getResAct() {
-        return resAct;
+    public void setProfile(
+            SyncProfile<T, A> profile) {
+        this.profile = profile;
     }
 
-    public void setResAct(final ConflictResolutionAction resAct) {
-        this.resAct = resAct;
+    public SyncProfile<T, A> getProfile() {
+        return profile;
     }
 }

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/DBPasswordSyncActions.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/DBPasswordSyncActions.java?rev=1605361&r1=1605360&r2=1605361&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/DBPasswordSyncActions.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/DBPasswordSyncActions.java Wed Jun 25 12:05:42 2014
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.core.sync.impl;
 
+import org.apache.syncope.core.sync.SyncProfile;
 import java.util.Iterator;
 
 import org.apache.syncope.common.to.AbstractAttributableTO;
@@ -58,16 +59,16 @@ public class DBPasswordSyncActions exten
     @Transactional(readOnly = true)
     @Override
     public <T extends AbstractAttributableTO> SyncDelta beforeCreate(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final SyncDelta delta,
             final T subject) throws JobExecutionException {
 
         if (subject instanceof UserTO) {
             String password = ((UserTO) subject).getPassword();
             if (password != null) {
-                Connector connector = handler.getConnector();
+                Connector connector = profile.getConnector();
                 ConnInstance connInstance = connector.getActiveConnInstance();
-                
+
                 String cipherAlgorithm = getCipherAlgorithm(connInstance);
                 if (!CLEARTEXT.equals(cipherAlgorithm)) {
                     try {
@@ -83,7 +84,7 @@ public class DBPasswordSyncActions exten
 
         return delta;
     }
-    
+
     private String getCipherAlgorithm(ConnInstance connInstance) {
         String cipherAlgorithm = CLEARTEXT;
         for (Iterator<ConnConfProperty> propertyIterator = connInstance.getConfiguration().iterator();
@@ -102,7 +103,7 @@ public class DBPasswordSyncActions exten
     @Transactional(readOnly = true)
     @Override
     public <T extends AbstractAttributableTO> void after(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final SyncDelta delta,
             final T subject,
             final SyncResult result) throws JobExecutionException {
@@ -116,5 +117,4 @@ public class DBPasswordSyncActions exten
             cipher = null;
         }
     }
-
 }

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/LDAPMembershipSyncActions.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/LDAPMembershipSyncActions.java?rev=1605361&r1=1605360&r2=1605361&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/LDAPMembershipSyncActions.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/LDAPMembershipSyncActions.java Wed Jun 25 12:05:42 2014
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.core.sync.impl;
 
+import org.apache.syncope.core.sync.SyncProfile;
+import org.apache.syncope.core.sync.SyncUtilities;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -34,6 +36,7 @@ import org.apache.syncope.common.types.A
 import org.apache.syncope.common.types.ConnConfProperty;
 import org.apache.syncope.core.audit.AuditManager;
 import org.apache.syncope.core.notification.NotificationManager;
+import org.apache.syncope.core.persistence.beans.AbstractSyncTask;
 import org.apache.syncope.core.persistence.beans.ConnInstance;
 import org.apache.syncope.core.persistence.beans.ExternalResource;
 import org.apache.syncope.core.persistence.beans.PropagationTask;
@@ -87,6 +90,9 @@ public class LDAPMembershipSyncActions e
     @Autowired
     private AuditManager auditManager;
 
+    @Autowired
+    private SyncUtilities syncUtilities;
+
     protected Map<Long, Long> membersBeforeRoleUpdate = Collections.<Long, Long>emptyMap();
 
     /**
@@ -121,7 +127,7 @@ public class LDAPMembershipSyncActions e
     @Transactional(readOnly = true)
     @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) throws JobExecutionException {
@@ -139,7 +145,7 @@ public class LDAPMembershipSyncActions e
             }
         }
 
-        return super.beforeUpdate(handler, delta, subject, subjectMod);
+        return super.beforeUpdate(profile, delta, subject, subjectMod);
     }
 
     /**
@@ -255,15 +261,18 @@ public class LDAPMembershipSyncActions e
      * @param roleTO role after modification performed by the handler
      * @throws JobExecutionException if anything goes wrong
      */
-    protected void synchronizeMemberships(final SyncopeSyncResultHandler handler, final SyncDelta delta,
-            final RoleTO roleTO) throws JobExecutionException {
-
-        final SyncTask task = handler.getSyncTask();
+    protected void synchronizeMemberships(
+            final SyncProfile<?, ?> profile, final SyncDelta delta, final RoleTO roleTO) throws JobExecutionException {
+        final AbstractSyncTask task = profile.getSyncTask();
         final ExternalResource resource = task.getResource();
-        final Connector connector = handler.getConnector();
+        final Connector connector = profile.getConnector();
 
         for (Object membValue : getMembAttrValues(delta, connector)) {
-            Long userId = handler.findMatchingAttributableId(ObjectClass.ACCOUNT, membValue.toString());
+            Long userId = syncUtilities.findMatchingAttributableId(
+                    ObjectClass.ACCOUNT,
+                    membValue.toString(),
+                    profile.getSyncTask().getResource(),
+                    profile.getConnector());
             if (userId != null) {
                 UserMod userMod = getUserMod(userId, roleTO);
                 userUpdate(userMod, resource.getName());
@@ -285,20 +294,19 @@ public class LDAPMembershipSyncActions e
      */
     @Override
     public <T extends AbstractAttributableTO> void after(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final SyncDelta delta,
             final T subject,
             final SyncResult result) throws JobExecutionException {
 
-        if (!(handler instanceof SyncopeSyncResultHandler)) {
+        if (!(profile.getSyncTask() instanceof SyncTask)) {
             return;
         }
 
-        SyncopeSyncResultHandler intHandler = (SyncopeSyncResultHandler) handler;
-        if (!(subject instanceof RoleTO) || intHandler.getSyncTask().getResource().getUmapping() == null) {
-            super.after(handler, delta, subject, result);
+        if (!(subject instanceof RoleTO) || profile.getSyncTask().getResource().getUmapping() == null) {
+            super.after(profile, delta, subject, result);
         } else {
-            synchronizeMemberships(intHandler, delta, (RoleTO) subject);
+            synchronizeMemberships(profile, delta, (RoleTO) subject);
         }
     }
 }

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/LDAPPasswordSyncActions.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/LDAPPasswordSyncActions.java?rev=1605361&r1=1605360&r2=1605361&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/LDAPPasswordSyncActions.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/LDAPPasswordSyncActions.java Wed Jun 25 12:05:42 2014
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.core.sync.impl;
 
+import org.apache.syncope.core.sync.SyncProfile;
 import org.apache.syncope.common.to.AbstractAttributableTO;
 import org.apache.syncope.common.to.UserTO;
 import org.apache.syncope.common.types.CipherAlgorithm;
@@ -52,7 +53,7 @@ public class LDAPPasswordSyncActions ext
     @Transactional(readOnly = true)
     @Override
     public <T extends AbstractAttributableTO> SyncDelta beforeCreate(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final SyncDelta delta,
             final T subject) throws JobExecutionException {
 
@@ -80,7 +81,7 @@ public class LDAPPasswordSyncActions ext
     @Transactional(readOnly = true)
     @Override
     public <T extends AbstractAttributableTO> void after(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final SyncDelta delta,
             final T subject,
             final SyncResult result) throws JobExecutionException {
@@ -98,5 +99,4 @@ public class LDAPPasswordSyncActions ext
             cipher = null;
         }
     }
-
 }

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/PushJob.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/PushJob.java?rev=1605361&r1=1605360&r2=1605361&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/PushJob.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/PushJob.java Wed Jun 25 12:05:42 2014
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.core.sync.impl;
 
+import org.apache.syncope.core.sync.SyncProfile;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -87,18 +88,29 @@ public class PushJob extends AbstractSyn
 
         final Set<Long> authorizations = EntitlementUtil.getRoleIds(entitlementDAO.findAll());
 
-        final SyncopePushResultHandler handler =
-                (SyncopePushResultHandler) ((DefaultListableBeanFactory) ApplicationContextProvider.
+        final SyncProfile<PushTask, PushActions> profile =
+                new SyncProfile<PushTask, PushActions>(connector, pushTask);
+        profile.setActions(actions);
+        profile.setDryRun(dryRun);
+        profile.setResAct(syncPolicySpec.getConflictResolutionAction());
+        profile.setResults(results);
+
+        final UserPushResultHandler uhandler =
+                (UserPushResultHandler) ((DefaultListableBeanFactory) ApplicationContextProvider.
+                getApplicationContext().getBeanFactory()).createBean(
+                UserPushResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
+        uhandler.setProfile(profile);
+
+        final RolePushResultHandler rhandler =
+                (RolePushResultHandler) ((DefaultListableBeanFactory) ApplicationContextProvider.
                 getApplicationContext().getBeanFactory()).createBean(
-                        SyncopePushResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
-        handler.setConnector(connector);
-        handler.setDryRun(dryRun);
-        handler.setResults(results);
-        handler.setSyncTask(pushTask);
-        handler.setActions(actions);
+                RolePushResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
+        rhandler.setProfile(profile);
 
-        for (PushActions action : actions) {
-            action.beforeAll(handler);
+        if (!profile.isDryRun()) {
+            for (PushActions action : actions) {
+                action.beforeAll(profile);
+            }
         }
 
         if (uMapping != null) {
@@ -109,7 +121,7 @@ public class PushJob extends AbstractSyn
                 for (SyncopeUser localUser : localUsers) {
                     try {
                         // user propagation
-                        handler.handle(localUser);
+                        uhandler.handle(localUser);
                     } catch (Exception e) {
                         LOG.warn("Failure pushing user '{}' on '{}'", localUser, pushTask.getResource());
                         if (!continueOnError()) {
@@ -126,7 +138,7 @@ public class PushJob extends AbstractSyn
             for (SyncopeRole localRole : localRoles) {
                 try {
                     // role propagation
-                    handler.handle(localRole);
+                    rhandler.handle(localRole);
                 } catch (Exception e) {
                     LOG.warn("Failure pushing role '{}' on '{}'", localRole, pushTask.getResource());
                     if (!continueOnError()) {
@@ -136,8 +148,10 @@ public class PushJob extends AbstractSyn
             }
         }
 
-        for (PushActions action : actions) {
-            action.afterAll(handler, results);
+        if (!profile.isDryRun()) {
+            for (PushActions action : actions) {
+                action.afterAll(profile, results);
+            }
         }
 
         final String result = createReport(results, pushTask.getResource().getSyncTraceLevel(), dryRun);

Added: syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/RolePushResultHandler.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/RolePushResultHandler.java?rev=1605361&view=auto
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/RolePushResultHandler.java (added)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/RolePushResultHandler.java Wed Jun 25 12:05:42 2014
@@ -0,0 +1,22 @@
+/*
+ * 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;
+
+public class RolePushResultHandler extends AbstractSubjectPushResultHandler {
+}

Added: syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/RoleSyncResultHandler.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/RoleSyncResultHandler.java?rev=1605361&view=auto
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/RoleSyncResultHandler.java (added)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/RoleSyncResultHandler.java Wed Jun 25 12:05:42 2014
@@ -0,0 +1,150 @@
+/*
+ * 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 static org.apache.syncope.core.sync.impl.AbstractSyncopeResultHandler.LOG;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.mod.AbstractSubjectMod;
+import org.apache.syncope.common.mod.AttributeMod;
+import org.apache.syncope.common.mod.RoleMod;
+import org.apache.syncope.common.to.AbstractSubjectTO;
+import org.apache.syncope.common.to.AttributeTO;
+import org.apache.syncope.common.to.RoleTO;
+import org.apache.syncope.common.types.AttributableType;
+import org.apache.syncope.core.persistence.beans.PropagationTask;
+import org.apache.syncope.core.sync.SyncResult;
+import org.apache.syncope.core.util.AttributableUtil;
+import org.apache.syncope.core.util.EntitlementUtil;
+import org.apache.syncope.core.workflow.WorkflowResult;
+import org.identityconnectors.framework.common.objects.SyncDelta;
+
+public class RoleSyncResultHandler extends AbstractSubjectSyncResultHandler {
+
+    protected Map<Long, String> roleOwnerMap = new HashMap<Long, String>();
+
+    public Map<Long, String> getRoleOwnerMap() {
+        return this.roleOwnerMap;
+    }
+
+    @Override
+    protected AttributableUtil getAttributableUtil() {
+        return AttributableUtil.getInstance(AttributableType.ROLE);
+    }
+
+    @Override
+    protected String getName(final AbstractSubjectTO subjectTO) {
+        return RoleTO.class.cast(subjectTO).getName();
+    }
+
+    @Override
+    protected AbstractSubjectTO getSubjectTO(final long id) {
+        return roleDataBinder.getRoleTO(id);
+    }
+
+    @Override
+    protected AbstractSubjectMod getSubjectMod(
+            final AbstractSubjectTO subjectTO, final SyncDelta delta) {
+
+        return connObjectUtil.getAttributableMod(
+                subjectTO.getId(),
+                delta.getObject(),
+                subjectTO,
+                profile.getSyncTask(),
+                AttributableUtil.getInstance(AttributableType.ROLE));
+    }
+
+    @Override
+    protected AbstractSubjectTO create(
+            final AbstractSubjectTO subjectTO, final SyncDelta _delta, final SyncResult result) {
+
+        RoleTO roleTO = RoleTO.class.cast(subjectTO);
+
+        WorkflowResult<Long> created = rwfAdapter.create(roleTO);
+        AttributeTO roleOwner = roleTO.getAttrMap().get(StringUtils.EMPTY);
+        if (roleOwner != null) {
+            roleOwnerMap.put(created.getResult(), roleOwner.getValues().iterator().next());
+        }
+
+        EntitlementUtil.extendAuthContext(created.getResult());
+
+        List<PropagationTask> tasks = propagationManager.getRoleCreateTaskIds(created,
+                roleTO.getVirAttrs(), Collections.singleton(profile.getSyncTask().getResource().getName()));
+
+        taskExecutor.execute(tasks);
+
+        roleTO = roleDataBinder.getRoleTO(created.getResult());
+
+        result.setId(created.getResult());
+        result.setName(getName(subjectTO));
+
+        return roleTO;
+    }
+
+    @Override
+    protected AbstractSubjectTO update(
+            final AbstractSubjectTO before,
+            final AbstractSubjectMod subjectMod,
+            final SyncDelta delta,
+            final SyncResult result)
+            throws Exception {
+
+        RoleMod roleMod = RoleMod.class.cast(subjectMod);
+
+        final WorkflowResult<Long> updated = rwfAdapter.update(roleMod);
+        String roleOwner = null;
+        for (AttributeMod attrMod : roleMod.getAttrsToUpdate()) {
+            if (attrMod.getSchema().isEmpty()) {
+                roleOwner = attrMod.getValuesToBeAdded().iterator().next();
+            }
+        }
+        if (roleOwner != null) {
+            roleOwnerMap.put(updated.getResult(), roleOwner);
+        }
+
+        List<PropagationTask> tasks = propagationManager.getRoleUpdateTaskIds(updated,
+                roleMod.getVirAttrsToRemove(),
+                roleMod.getVirAttrsToUpdate(),
+                Collections.singleton(profile.getSyncTask().getResource().getName()));
+
+        taskExecutor.execute(tasks);
+
+        final RoleTO after = roleDataBinder.getRoleTO(updated.getResult());
+        result.setName(getName(after));
+
+        return after;
+    }
+
+    @Override
+    protected void delete(Long id) {
+        try {
+            taskExecutor.execute(
+                    propagationManager.getRoleDeleteTaskIds(id, profile.getSyncTask().getResource().getName()));
+        } catch (Exception 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 user " + id, e);
+        }
+
+        rwfAdapter.delete(id);
+    }
+}

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/SyncJob.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/SyncJob.java?rev=1605361&r1=1605360&r2=1605361&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/SyncJob.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/SyncJob.java Wed Jun 25 12:05:42 2014
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.core.sync.impl;
 
+import org.apache.syncope.core.sync.SyncProfile;
+import org.apache.syncope.core.sync.SyncUtilities;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -57,10 +59,13 @@ public class SyncJob extends AbstractSyn
     @Autowired
     private RoleWorkflowAdapter rwfAdapter;
 
-    protected void setRoleOwners(final SyncopeSyncResultHandler handler)
+    @Autowired
+    protected SyncUtilities syncUtilities;
+
+    protected void setRoleOwners(final RoleSyncResultHandler rhandler)
             throws UnauthorizedRoleException, NotFoundException {
 
-        for (Map.Entry<Long, String> entry : handler.getRoleOwnerMap().entrySet()) {
+        for (Map.Entry<Long, String> entry : rhandler.getRoleOwnerMap().entrySet()) {
             RoleMod roleMod = new RoleMod();
             roleMod.setId(entry.getKey());
 
@@ -68,9 +73,19 @@ public class SyncJob extends AbstractSyn
                 roleMod.setRoleOwner(null);
                 roleMod.setUserOwner(null);
             } else {
-                Long userId = handler.findMatchingAttributableId(ObjectClass.ACCOUNT, entry.getValue());
+                Long userId = syncUtilities.findMatchingAttributableId(
+                        ObjectClass.ACCOUNT,
+                        entry.getValue(),
+                        rhandler.getProfile().getSyncTask().getResource(),
+                        rhandler.getProfile().getConnector());
+
                 if (userId == null) {
-                    Long roleId = handler.findMatchingAttributableId(ObjectClass.GROUP, entry.getValue());
+                    Long roleId = syncUtilities.findMatchingAttributableId(
+                            ObjectClass.GROUP,
+                            entry.getValue(),
+                            rhandler.getProfile().getSyncTask().getResource(),
+                            rhandler.getProfile().getConnector());
+
                     if (roleId != null) {
                         roleMod.setRoleOwner(new ReferenceMod(roleId));
                     }
@@ -95,21 +110,33 @@ public class SyncJob extends AbstractSyn
 
         final List<SyncResult> results = new ArrayList<SyncResult>();
 
-        // Prepare handler for SyncDelta objects
-        final SyncopeSyncResultHandler handler =
-                (SyncopeSyncResultHandler) ((DefaultListableBeanFactory) ApplicationContextProvider.
+        final SyncProfile<SyncTask, SyncActions> profile =
+                new SyncProfile<SyncTask, SyncActions>(connector, syncTask);
+        profile.setActions(actions);
+        profile.setDryRun(dryRun);
+        profile.setResAct(syncPolicySpec.getConflictResolutionAction());
+        profile.setResults(results);
+
+        // Prepare handler for SyncDelta objects (users)
+        final UserSyncResultHandler uhandler =
+                (UserSyncResultHandler) ((DefaultListableBeanFactory) ApplicationContextProvider.
                 getApplicationContext().getBeanFactory()).createBean(
-                        SyncopeSyncResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
-        handler.setConnector(connector);
-        handler.setActions(actions);
-        handler.setDryRun(dryRun);
-        handler.setResAct(syncPolicySpec.getConflictResolutionAction());
-        handler.setResults(results);
-        handler.setSyncTask(syncTask);
+                UserSyncResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
+        uhandler.setProfile(profile);
 
-        for (SyncActions action : actions) {
-            action.beforeAll(handler);
+        // Prepare handler for SyncDelta objects (roles/groups)
+        final RoleSyncResultHandler rhandler =
+                (RoleSyncResultHandler) ((DefaultListableBeanFactory) ApplicationContextProvider.
+                getApplicationContext().getBeanFactory()).createBean(
+                RoleSyncResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
+        rhandler.setProfile(profile);
+
+        if (!profile.isDryRun()) {
+            for (SyncActions action : actions) {
+                action.beforeAll(profile);
+            }
         }
+
         try {
             SyncToken latestUSyncToken = null;
             if (uMapping != null && !syncTask.isFullReconciliation()) {
@@ -122,20 +149,20 @@ public class SyncJob extends AbstractSyn
 
             if (syncTask.isFullReconciliation()) {
                 if (uMapping != null) {
-                    connector.getAllObjects(ObjectClass.ACCOUNT, handler,
+                    connector.getAllObjects(ObjectClass.ACCOUNT, uhandler,
                             connector.getOperationOptions(uMapping.getItems()));
                 }
                 if (rMapping != null) {
-                    connector.getAllObjects(ObjectClass.GROUP, handler,
+                    connector.getAllObjects(ObjectClass.GROUP, rhandler,
                             connector.getOperationOptions(rMapping.getItems()));
                 }
             } else {
                 if (uMapping != null) {
-                    connector.sync(ObjectClass.ACCOUNT, syncTask.getResource().getUsyncToken(), handler,
+                    connector.sync(ObjectClass.ACCOUNT, syncTask.getResource().getUsyncToken(), uhandler,
                             connector.getOperationOptions(uMapping.getItems()));
                 }
                 if (rMapping != null) {
-                    connector.sync(ObjectClass.GROUP, syncTask.getResource().getRsyncToken(), handler,
+                    connector.sync(ObjectClass.GROUP, syncTask.getResource().getRsyncToken(), rhandler,
                             connector.getOperationOptions(rMapping.getItems()));
                 }
             }
@@ -159,13 +186,15 @@ public class SyncJob extends AbstractSyn
         }
 
         try {
-            setRoleOwners(handler);
+            setRoleOwners(rhandler);
         } catch (Exception e) {
             LOG.error("While setting role owners", e);
         }
 
-        for (SyncActions action : actions) {
-            action.afterAll(handler, results);
+        if (!profile.isDryRun()) {
+            for (SyncActions action : actions) {
+                action.afterAll(profile, results);
+            }
         }
 
         final String result = createReport(results, syncTask.getResource().getSyncTraceLevel(), dryRun);

Added: syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/UserPushResultHandler.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/UserPushResultHandler.java?rev=1605361&view=auto
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/UserPushResultHandler.java (added)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/UserPushResultHandler.java Wed Jun 25 12:05:42 2014
@@ -0,0 +1,22 @@
+/*
+ * 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;
+
+public class UserPushResultHandler extends AbstractSubjectPushResultHandler {
+}

Added: syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/UserSyncResultHandler.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/UserSyncResultHandler.java?rev=1605361&view=auto
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/UserSyncResultHandler.java (added)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/UserSyncResultHandler.java Wed Jun 25 12:05:42 2014
@@ -0,0 +1,164 @@
+/*
+ * 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 static org.apache.syncope.core.sync.impl.AbstractSyncopeResultHandler.LOG;
+import java.util.AbstractMap;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import org.apache.syncope.common.mod.AbstractSubjectMod;
+import org.apache.syncope.common.mod.UserMod;
+import org.apache.syncope.common.to.AbstractSubjectTO;
+import org.apache.syncope.common.to.UserTO;
+import org.apache.syncope.common.types.AttributableType;
+import org.apache.syncope.core.persistence.beans.PropagationTask;
+import org.apache.syncope.core.persistence.beans.user.SyncopeUser;
+import org.apache.syncope.core.propagation.PropagationByResource;
+import org.apache.syncope.core.sync.SyncResult;
+import org.apache.syncope.core.util.AttributableUtil;
+import org.apache.syncope.core.workflow.WorkflowResult;
+import org.identityconnectors.framework.common.objects.SyncDelta;
+
+public class UserSyncResultHandler extends AbstractSubjectSyncResultHandler {
+
+    @Override
+    protected AttributableUtil getAttributableUtil() {
+        return AttributableUtil.getInstance(AttributableType.USER);
+    }
+
+    @Override
+    protected String getName(final AbstractSubjectTO subjectTO) {
+        return UserTO.class.cast(subjectTO).getUsername();
+    }
+
+    @Override
+    protected AbstractSubjectTO getSubjectTO(final long id) {
+        return userDataBinder.getUserTO(id);
+    }
+
+    @Override
+    protected AbstractSubjectMod getSubjectMod(
+            final AbstractSubjectTO subjectTO, final SyncDelta delta) {
+
+        return connObjectUtil.getAttributableMod(
+                subjectTO.getId(),
+                delta.getObject(),
+                subjectTO,
+                profile.getSyncTask(),
+                AttributableUtil.getInstance(AttributableType.USER));
+    }
+
+    @Override
+    protected AbstractSubjectTO create(
+            final AbstractSubjectTO subjectTO, final SyncDelta delta, final SyncResult result) {
+
+        UserTO userTO = UserTO.class.cast(subjectTO);
+
+        Boolean enabled = syncUtilities.readEnabled(delta.getObject(), profile.getSyncTask());
+        WorkflowResult<Map.Entry<Long, Boolean>> created =
+                uwfAdapter.create(userTO, true, enabled);
+
+        List<PropagationTask> tasks = propagationManager.getUserCreateTaskIds(created,
+                userTO.getPassword(), userTO.getVirAttrs(),
+                Collections.singleton(profile.getSyncTask().getResource().getName()),
+                userTO.getMemberships());
+
+        taskExecutor.execute(tasks);
+
+        userTO = userDataBinder.getUserTO(created.getResult().getKey());
+
+        result.setId(created.getResult().getKey());
+        result.setName(getName(subjectTO));
+
+        return userTO;
+    }
+
+    @Override
+    protected AbstractSubjectTO update(
+            final AbstractSubjectTO before,
+            final AbstractSubjectMod subjectMod,
+            final SyncDelta delta, final SyncResult result)
+            throws Exception {
+
+        final UserMod userMod = UserMod.class.cast(subjectMod);
+
+        WorkflowResult<Map.Entry<UserMod, Boolean>> updated;
+        try {
+            updated = uwfAdapter.update(userMod);
+        } catch (Exception e) {
+            LOG.error("Update of user {} failed, trying to sync its status anyway (if configured)", before.getId(), e);
+
+            result.setStatus(SyncResult.Status.FAILURE);
+            result.setMessage("Update failed, trying to sync status anyway (if configured)\n" + e.getMessage());
+
+            updated = new WorkflowResult<Map.Entry<UserMod, Boolean>>(
+                    new AbstractMap.SimpleEntry<UserMod, Boolean>(userMod, false), new PropagationByResource(),
+                    new HashSet<String>());
+        }
+
+        final Boolean enabled = syncUtilities.readEnabled(delta.getObject(), profile.getSyncTask());
+        if (enabled != null) {
+            SyncopeUser user = userDAO.find(before.getId());
+
+            WorkflowResult<Long> enableUpdate = null;
+            if (user.isSuspended() == null) {
+                enableUpdate = uwfAdapter.activate(before.getId(), null);
+            } else if (enabled && user.isSuspended()) {
+                enableUpdate = uwfAdapter.reactivate(before.getId());
+            } else if (!enabled && !user.isSuspended()) {
+                enableUpdate = uwfAdapter.suspend(before.getId());
+            }
+
+            if (enableUpdate != null) {
+                if (enableUpdate.getPropByRes() != null) {
+                    updated.getPropByRes().merge(enableUpdate.getPropByRes());
+                    updated.getPropByRes().purge();
+                }
+                updated.getPerformedTasks().addAll(enableUpdate.getPerformedTasks());
+            }
+        }
+
+        final List<PropagationTask> tasks = propagationManager.getUserUpdateTaskIds(
+                updated, updated.getResult().getKey().getPassword() != null,
+                Collections.singleton(profile.getSyncTask().getResource().getName()));
+
+        taskExecutor.execute(tasks);
+
+        final UserTO after = userDataBinder.getUserTO(updated.getResult().getKey().getId());
+
+        result.setName(getName(after));
+        return after;
+    }
+
+    @Override
+    protected void delete(final Long id) {
+        try {
+            taskExecutor.execute(
+                    propagationManager.getUserDeleteTaskIds(id, profile.getSyncTask().getResource().getName()));
+        } catch (Exception 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 user " + id, e);
+        }
+
+        uwfAdapter.delete(id);
+    }
+}

Modified: syncope/trunk/core/src/main/resources/syncopeContext.xml
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/resources/syncopeContext.xml?rev=1605361&r1=1605360&r2=1605361&view=diff
==============================================================================
--- syncope/trunk/core/src/main/resources/syncopeContext.xml (original)
+++ syncope/trunk/core/src/main/resources/syncopeContext.xml Wed Jun 25 12:05:42 2014
@@ -89,6 +89,8 @@ under the License.
   <bean id="connectorFacadeProxy" class="org.apache.syncope.core.propagation.impl.ConnectorFacadeProxy" scope="prototype"/>
   <bean id="asyncConnFacade" class="org.apache.syncope.core.propagation.impl.AsyncConnectorFacade" scope="singleton"/>
 
+  <bean id="syncUtilities" class="org.apache.syncope.core.sync.SyncUtilities"/>
+  
   <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
     <property name="defaultEncoding" value="${smtpEncoding}"/>
     <property name="host" value="${smtpHost}"/>

Modified: syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/RoleTestITCase.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/RoleTestITCase.java?rev=1605361&r1=1605360&r2=1605361&view=diff
==============================================================================
--- syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/RoleTestITCase.java (original)
+++ syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/RoleTestITCase.java Wed Jun 25 12:05:42 2014
@@ -36,6 +36,7 @@ import org.apache.commons.lang3.StringUt
 import org.apache.syncope.client.SyncopeClient;
 import org.apache.syncope.common.SyncopeClientException;
 import org.apache.syncope.common.mod.AttributeMod;
+import org.apache.syncope.common.mod.ReferenceMod;
 import org.apache.syncope.common.mod.RoleMod;
 import org.apache.syncope.common.reqres.BulkActionResult;
 import org.apache.syncope.common.reqres.PagedResult;
@@ -60,6 +61,7 @@ import org.apache.syncope.common.util.Co
 import org.apache.syncope.common.wrap.ResourceName;
 import org.identityconnectors.framework.common.objects.Name;
 import org.junit.FixMethodOrder;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runners.MethodSorters;
 
@@ -106,6 +108,7 @@ public class RoleTestITCase extends Abst
     }
 
     @Test
+    @Ignore
     public void create() {
         RoleTO roleTO = buildRoleTO("lastRole");
         roleTO.getRVirAttrTemplates().add("rvirtualdata");
@@ -132,6 +135,13 @@ public class RoleTestITCase extends Abst
                 resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, roleTO.getId());
         assertNotNull(connObjectTO);
         assertNotNull(connObjectTO.getAttrMap().get("owner"));
+
+        // SYNCOPE-515: remove ownership
+        final RoleMod roleMod = new RoleMod();
+        roleMod.setId(roleTO.getId());
+        roleMod.setRoleOwner(new ReferenceMod());
+
+        assertNull(updateRole(roleMod).getRoleOwner());
     }
 
     @Test

Modified: syncope/trunk/core/src/test/java/org/apache/syncope/core/sync/TestSyncActions.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/test/java/org/apache/syncope/core/sync/TestSyncActions.java?rev=1605361&r1=1605360&r2=1605361&view=diff
==============================================================================
--- syncope/trunk/core/src/test/java/org/apache/syncope/core/sync/TestSyncActions.java (original)
+++ syncope/trunk/core/src/test/java/org/apache/syncope/core/sync/TestSyncActions.java Wed Jun 25 12:05:42 2014
@@ -22,7 +22,6 @@ import org.apache.syncope.common.mod.Abs
 import org.apache.syncope.common.mod.AttributeMod;
 import org.apache.syncope.common.to.AbstractAttributableTO;
 import org.apache.syncope.common.to.AttributeTO;
-import org.apache.syncope.core.sync.impl.AbstractSyncopeResultHandler;
 import org.identityconnectors.framework.common.objects.SyncDelta;
 import org.quartz.JobExecutionException;
 
@@ -32,7 +31,7 @@ public class TestSyncActions extends Def
 
     @Override
     public <T extends AbstractAttributableTO> SyncDelta beforeCreate(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final SyncDelta delta,
             final T subject) throws JobExecutionException {
 
@@ -55,7 +54,7 @@ public class TestSyncActions extends Def
 
     @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) throws JobExecutionException {