You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2017/09/22 08:15:56 UTC

[5/6] syncope git commit: [SYNCOPE-1212] Clearing and refactoring for easier extensions

http://git-wip-us.apache.org/repos/asf/syncope/blob/9c289134/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java
new file mode 100644
index 0000000..1040461
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java
@@ -0,0 +1,450 @@
+/*
+ * 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.provisioning.java.pushpull;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.apache.syncope.common.lib.to.RealmTO;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.AuditElements.Result;
+import org.apache.syncope.common.lib.types.MatchingRule;
+import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
+import org.apache.syncope.core.provisioning.api.PropagationByResource;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.common.lib.types.UnmatchingRule;
+import org.apache.syncope.core.persistence.api.entity.Realm;
+import org.apache.syncope.core.persistence.api.entity.resource.Item;
+import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
+import org.apache.syncope.core.persistence.api.entity.resource.OrgUnitItem;
+import org.apache.syncope.core.persistence.api.entity.task.PushTask;
+import org.apache.syncope.core.provisioning.api.MappingManager;
+import org.apache.syncope.core.provisioning.api.TimeoutException;
+import org.apache.syncope.core.provisioning.api.event.AfterHandlingEvent;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException;
+import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+import org.apache.syncope.core.provisioning.api.pushpull.PushActions;
+import org.apache.syncope.core.provisioning.api.pushpull.RealmPushResultHandler;
+import org.apache.syncope.core.provisioning.java.job.AfterHandlingJob;
+import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
+import org.identityconnectors.framework.common.objects.AttributeBuilder;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.quartz.SchedulerFactoryBean;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+public class DefaultRealmPushResultHandler
+        extends AbstractRealmResultHandler<PushTask, PushActions>
+        implements RealmPushResultHandler {
+
+    @Autowired
+    private MappingManager mappingManager;
+
+    @Autowired
+    private SchedulerFactoryBean scheduler;
+
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
+    @Override
+    public boolean handle(final String realmKey) {
+        Realm realm = null;
+        try {
+            realm = realmDAO.find(realmKey);
+            doHandle(realm);
+            return true;
+        } catch (IgnoreProvisionException e) {
+            ProvisioningReport result = new ProvisioningReport();
+            result.setOperation(ResourceOperation.NONE);
+            result.setAnyType(realm == null ? null : REALM_TYPE);
+            result.setStatus(ProvisioningReport.Status.IGNORE);
+            result.setKey(realmKey);
+            profile.getResults().add(result);
+
+            LOG.warn("Ignoring during push", e);
+            return true;
+        } catch (JobExecutionException e) {
+            LOG.error("Push failed", e);
+            return false;
+        }
+    }
+
+    private void reportPropagation(final ProvisioningReport result, final PropagationReporter reporter) {
+        if (!reporter.getStatuses().isEmpty()) {
+            result.setStatus(toProvisioningReportStatus(reporter.getStatuses().get(0).getStatus()));
+            result.setMessage(reporter.getStatuses().get(0).getFailureReason());
+        }
+    }
+
+    private Realm update(final RealmTO realmTO, final ProvisioningReport result) {
+        Realm realm = realmDAO.findByFullPath(realmTO.getFullPath());
+        PropagationByResource propByRes = binder.update(realm, realmTO);
+        realm = realmDAO.save(realm);
+
+        PropagationReporter reporter = taskExecutor.execute(
+                propagationManager.createTasks(realm, propByRes, null), false);
+        reportPropagation(result, reporter);
+
+        return realm;
+    }
+
+    private void deprovision(final Realm realm, final ProvisioningReport result) {
+        List<String> noPropResources = new ArrayList<>(realm.getResourceKeys());
+        noPropResources.remove(profile.getTask().getResource().getKey());
+
+        PropagationByResource propByRes = new PropagationByResource();
+        propByRes.addAll(ResourceOperation.DELETE, realm.getResourceKeys());
+
+        PropagationReporter reporter = taskExecutor.execute(
+                propagationManager.createTasks(realm, propByRes, noPropResources), false);
+        reportPropagation(result, reporter);
+    }
+
+    private void provision(final Realm realm, final ProvisioningReport result) {
+        List<String> noPropResources = new ArrayList<>(realm.getResourceKeys());
+        noPropResources.remove(profile.getTask().getResource().getKey());
+
+        PropagationByResource propByRes = new PropagationByResource();
+        propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
+
+        PropagationReporter reporter = taskExecutor.execute(
+                propagationManager.createTasks(realm, propByRes, noPropResources), false);
+        reportPropagation(result, reporter);
+    }
+
+    private void link(final Realm realm, final boolean unlink, final ProvisioningReport result) {
+        RealmTO realmTO = binder.getRealmTO(realm, true);
+        if (unlink) {
+            realmTO.getResources().remove(profile.getTask().getResource().getKey());
+        } else {
+            realmTO.getResources().add(profile.getTask().getResource().getKey());
+        }
+
+        update(realmTO, result);
+    }
+
+    private void unassign(final Realm realm, final ProvisioningReport result) {
+        RealmTO realmTO = binder.getRealmTO(realm, true);
+        realmTO.getResources().remove(profile.getTask().getResource().getKey());
+
+        deprovision(update(realmTO, result), result);
+    }
+
+    private void assign(final Realm realm, final ProvisioningReport result) {
+        RealmTO realmTO = binder.getRealmTO(realm, true);
+        realmTO.getResources().add(profile.getTask().getResource().getKey());
+
+        provision(update(realmTO, result), result);
+    }
+
+    protected ConnectorObject getRemoteObject(
+            final ObjectClass objectClass,
+            final String connObjectKey,
+            final String connObjectKeyValue,
+            final Iterator<? extends Item> iterator) {
+
+        ConnectorObject obj = null;
+        try {
+            obj = profile.getConnector().getObject(
+                    objectClass,
+                    AttributeBuilder.build(connObjectKey, connObjectKeyValue),
+                    MappingUtils.buildOperationOptions(iterator));
+        } catch (TimeoutException toe) {
+            LOG.debug("Request timeout", toe);
+            throw toe;
+        } catch (RuntimeException ignore) {
+            LOG.debug("While resolving {}", connObjectKeyValue, ignore);
+        }
+
+        return obj;
+    }
+
+    private void doHandle(final Realm realm) throws JobExecutionException {
+        ProvisioningReport result = new ProvisioningReport();
+        profile.getResults().add(result);
+
+        result.setKey(realm.getKey());
+        result.setAnyType(REALM_TYPE);
+        result.setName(realm.getFullPath());
+
+        LOG.debug("Propagating Realm with key {} towards {}", realm.getKey(), profile.getTask().getResource());
+
+        Object output = null;
+        Result resultStatus = null;
+
+        // Try to read remote object BEFORE any actual operation
+        OrgUnit orgUnit = profile.getTask().getResource().getOrgUnit();
+        Optional<? extends OrgUnitItem> connObjectKey = orgUnit.getConnObjectKeyItem();
+        String connObjecKeyValue = mappingManager.getConnObjectKeyValue(realm, orgUnit);
+
+        ConnectorObject beforeObj = getRemoteObject(
+                orgUnit.getObjectClass(),
+                connObjectKey.get().getExtAttrName(),
+                connObjecKeyValue,
+                orgUnit.getItems().iterator());
+
+        if (profile.isDryRun()) {
+            if (beforeObj == null) {
+                result.setOperation(toResourceOperation(profile.getTask().getUnmatchingRule()));
+            } else {
+                result.setOperation(toResourceOperation(profile.getTask().getMatchingRule()));
+            }
+            result.setStatus(ProvisioningReport.Status.SUCCESS);
+        } else {
+            String operation = beforeObj == null
+                    ? UnmatchingRule.toEventName(profile.getTask().getUnmatchingRule())
+                    : MatchingRule.toEventName(profile.getTask().getMatchingRule());
+
+            boolean notificationsAvailable = notificationManager.notificationsAvailable(
+                    AuditElements.EventCategoryType.PUSH,
+                    REALM_TYPE.toLowerCase(),
+                    profile.getTask().getResource().getKey(),
+                    operation);
+            boolean auditRequested = auditManager.auditRequested(
+                    AuditElements.EventCategoryType.PUSH,
+                    REALM_TYPE.toLowerCase(),
+                    profile.getTask().getResource().getKey(),
+                    operation);
+            try {
+                if (beforeObj == null) {
+                    result.setOperation(toResourceOperation(profile.getTask().getUnmatchingRule()));
+
+                    switch (profile.getTask().getUnmatchingRule()) {
+                        case ASSIGN:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeAssign(profile, realm);
+                            }
+
+                            if (!profile.getTask().isPerformCreate()) {
+                                LOG.debug("PushTask not configured for create");
+                                result.setStatus(ProvisioningReport.Status.IGNORE);
+                            } else {
+                                assign(realm, result);
+                            }
+
+                            break;
+
+                        case PROVISION:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeProvision(profile, realm);
+                            }
+
+                            if (!profile.getTask().isPerformCreate()) {
+                                LOG.debug("PushTask not configured for create");
+                                result.setStatus(ProvisioningReport.Status.IGNORE);
+                            } else {
+                                provision(realm, result);
+                            }
+
+                            break;
+
+                        case UNLINK:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeUnlink(profile, realm);
+                            }
+
+                            if (!profile.getTask().isPerformUpdate()) {
+                                LOG.debug("PushTask not configured for update");
+                                result.setStatus(ProvisioningReport.Status.IGNORE);
+                            } else {
+                                link(realm, true, result);
+                            }
+
+                            break;
+
+                        case IGNORE:
+                            LOG.debug("Ignored any: {}", realm);
+                            result.setStatus(ProvisioningReport.Status.IGNORE);
+                            break;
+
+                        default:
+                        // do nothing
+                    }
+                } else {
+                    result.setOperation(toResourceOperation(profile.getTask().getMatchingRule()));
+
+                    switch (profile.getTask().getMatchingRule()) {
+                        case UPDATE:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeUpdate(profile, realm);
+                            }
+                            if (!profile.getTask().isPerformUpdate()) {
+                                LOG.debug("PushTask not configured for update");
+                                result.setStatus(ProvisioningReport.Status.IGNORE);
+                            } else {
+                                update(binder.getRealmTO(realm, true), result);
+                            }
+
+                            break;
+
+                        case DEPROVISION:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeDeprovision(profile, realm);
+                            }
+
+                            if (!profile.getTask().isPerformDelete()) {
+                                LOG.debug("PushTask not configured for delete");
+                                result.setStatus(ProvisioningReport.Status.IGNORE);
+                            } else {
+                                deprovision(realm, result);
+                            }
+
+                            break;
+
+                        case UNASSIGN:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeUnassign(profile, realm);
+                            }
+
+                            if (!profile.getTask().isPerformDelete()) {
+                                LOG.debug("PushTask not configured for delete");
+                                result.setStatus(ProvisioningReport.Status.IGNORE);
+                            } else {
+                                unassign(realm, result);
+                            }
+
+                            break;
+
+                        case LINK:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeLink(profile, realm);
+                            }
+
+                            if (!profile.getTask().isPerformUpdate()) {
+                                LOG.debug("PushTask not configured for update");
+                                result.setStatus(ProvisioningReport.Status.IGNORE);
+                            } else {
+                                link(realm, false, result);
+                            }
+
+                            break;
+
+                        case UNLINK:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeUnlink(profile, realm);
+                            }
+
+                            if (!profile.getTask().isPerformUpdate()) {
+                                LOG.debug("PushTask not configured for update");
+                                result.setStatus(ProvisioningReport.Status.IGNORE);
+                            } else {
+                                link(realm, true, result);
+                            }
+
+                            break;
+
+                        case IGNORE:
+                            LOG.debug("Ignored any: {}", realm);
+                            result.setStatus(ProvisioningReport.Status.IGNORE);
+                            break;
+
+                        default:
+                        // do nothing
+                    }
+                }
+
+                for (PushActions action : profile.getActions()) {
+                    action.after(profile, realm, result);
+                }
+
+                if (result.getStatus() == null) {
+                    result.setStatus(ProvisioningReport.Status.SUCCESS);
+                }
+                resultStatus = AuditElements.Result.SUCCESS;
+                output = getRemoteObject(
+                        orgUnit.getObjectClass(),
+                        connObjectKey.get().getExtAttrName(),
+                        connObjecKeyValue,
+                        orgUnit.getItems().iterator());
+            } catch (IgnoreProvisionException e) {
+                throw e;
+            } catch (Exception e) {
+                result.setStatus(ProvisioningReport.Status.FAILURE);
+                result.setMessage(ExceptionUtils.getRootCauseMessage(e));
+                resultStatus = AuditElements.Result.FAILURE;
+                output = e;
+
+                LOG.warn("Error pushing {} towards {}", realm, profile.getTask().getResource(), e);
+
+                for (PushActions action : profile.getActions()) {
+                    action.onError(profile, realm, result, e);
+                }
+
+                throw new JobExecutionException(e);
+            } finally {
+                if (notificationsAvailable || auditRequested) {
+                    Map<String, Object> jobMap = new HashMap<>();
+                    jobMap.put(AfterHandlingEvent.JOBMAP_KEY, new AfterHandlingEvent(
+                            AuditElements.EventCategoryType.PUSH,
+                            REALM_TYPE.toLowerCase(),
+                            profile.getTask().getResource().getKey(),
+                            operation,
+                            resultStatus,
+                            beforeObj,
+                            output,
+                            realm));
+                    AfterHandlingJob.schedule(scheduler, jobMap);
+                }
+            }
+        }
+    }
+
+    private ResourceOperation toResourceOperation(final UnmatchingRule rule) {
+        switch (rule) {
+            case ASSIGN:
+            case PROVISION:
+                return ResourceOperation.CREATE;
+            default:
+                return ResourceOperation.NONE;
+        }
+    }
+
+    private ResourceOperation toResourceOperation(final MatchingRule rule) {
+        switch (rule) {
+            case UPDATE:
+                return ResourceOperation.UPDATE;
+            case DEPROVISION:
+            case UNASSIGN:
+                return ResourceOperation.DELETE;
+            default:
+                return ResourceOperation.NONE;
+        }
+    }
+
+    private ProvisioningReport.Status toProvisioningReportStatus(final PropagationTaskExecStatus status) {
+        switch (status) {
+            case FAILURE:
+                return ProvisioningReport.Status.FAILURE;
+
+            case SUCCESS:
+                return ProvisioningReport.Status.SUCCESS;
+
+            case CREATED:
+            case NOT_ATTEMPTED:
+            default:
+                return ProvisioningReport.Status.IGNORE;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/9c289134/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java
new file mode 100644
index 0000000..83a5401
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java
@@ -0,0 +1,134 @@
+/*
+ * 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.provisioning.java.pushpull;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.patch.AnyPatch;
+import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.PropagationStatus;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.provisioning.api.ProvisioningManager;
+import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
+import org.apache.syncope.core.provisioning.api.WorkflowResult;
+import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+import org.identityconnectors.framework.common.objects.SyncDelta;
+import org.apache.syncope.core.provisioning.api.pushpull.UserPullResultHandler;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public class DefaultUserPullResultHandler extends AbstractPullResultHandler implements UserPullResultHandler {
+
+    @Autowired
+    private UserProvisioningManager userProvisioningManager;
+
+    @Override
+    protected AnyUtils getAnyUtils() {
+        return anyUtilsFactory.getInstance(AnyTypeKind.USER);
+    }
+
+    @Override
+    protected String getName(final AnyTO anyTO) {
+        return UserTO.class.cast(anyTO).getUsername();
+    }
+
+    @Override
+    protected ProvisioningManager<?, ?> getProvisioningManager() {
+        return userProvisioningManager;
+    }
+
+    @Override
+    protected Any<?> getAny(final String key) {
+        try {
+            return userDAO.authFind(key);
+        } catch (Exception e) {
+            LOG.warn("Error retrieving user {}", key, e);
+            return null;
+        }
+    }
+
+    @Override
+    protected AnyTO getAnyTO(final String key) {
+        return userDataBinder.getUserTO(key);
+    }
+
+    @Override
+    protected AnyPatch newPatch(final String key) {
+        UserPatch patch = new UserPatch();
+        patch.setKey(key);
+        return patch;
+    }
+
+    @Override
+    protected WorkflowResult<? extends AnyPatch> update(final AnyPatch patch) {
+        WorkflowResult<Pair<UserPatch, Boolean>> update = uwfAdapter.update((UserPatch) patch);
+        return new WorkflowResult<>(update.getResult().getLeft(), update.getPropByRes(), update.getPerformedTasks());
+    }
+
+    @Override
+    protected AnyTO doCreate(final AnyTO anyTO, final SyncDelta delta) {
+        UserTO userTO = UserTO.class.cast(anyTO);
+
+        Boolean enabled = pullUtils.readEnabled(delta.getObject(), profile.getTask());
+        Map.Entry<String, List<PropagationStatus>> created =
+                userProvisioningManager.create(userTO, true, true, enabled,
+                        Collections.singleton(profile.getTask().getResource().getKey()), true);
+
+        return getAnyTO(created.getKey());
+    }
+
+    @Override
+    protected AnyPatch doUpdate(
+            final AnyTO before,
+            final AnyPatch anyPatch,
+            final SyncDelta delta,
+            final ProvisioningReport result) {
+
+        UserPatch userPatch = UserPatch.class.cast(anyPatch);
+        Boolean enabled = pullUtils.readEnabled(delta.getObject(), profile.getTask());
+
+        Pair<UserPatch, List<PropagationStatus>> updated = userProvisioningManager.update(
+                userPatch,
+                result,
+                enabled,
+                Collections.singleton(profile.getTask().getResource().getKey()),
+                true);
+
+        return updated.getLeft();
+    }
+
+    @Override
+    protected void doDelete(final AnyTypeKind kind, final String key) {
+        try {
+            userProvisioningManager.delete(
+                    key, Collections.<String>singleton(profile.getTask().getResource().getKey()), true);
+        } catch (Exception e) {
+            // A propagation failure doesn't imply a pull failure.
+            // The propagation exception status will be reported into the propagation task execution.
+            LOG.error("Could not propagate user " + key, e);
+        }
+
+        uwfAdapter.delete(key);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/9c289134/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java
new file mode 100644
index 0000000..70f3a00
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java
@@ -0,0 +1,99 @@
+/*
+ * 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.provisioning.java.pushpull;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.patch.AnyPatch;
+import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.provisioning.api.PropagationByResource;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.WorkflowResult;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+import org.apache.syncope.core.provisioning.api.pushpull.UserPushResultHandler;
+
+public class DefaultUserPushResultHandler extends AbstractPushResultHandler implements UserPushResultHandler {
+
+    @Override
+    protected AnyUtils getAnyUtils() {
+        return anyUtilsFactory.getInstance(AnyTypeKind.USER);
+    }
+
+    @Override
+    protected void provision(final Any<?> any, final Boolean enabled, final ProvisioningReport result) {
+        AnyTO before = getAnyTO(any.getKey());
+
+        List<String> noPropResources = new ArrayList<>(before.getResources());
+        noPropResources.remove(profile.getTask().getResource().getKey());
+
+        PropagationByResource propByRes = new PropagationByResource();
+        propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
+
+        PropagationReporter reporter = taskExecutor.execute(propagationManager.getUserCreateTasks(
+                before.getKey(),
+                null,
+                enabled,
+                propByRes,
+                before.getVirAttrs(),
+                noPropResources),
+                false);
+        reportPropagation(result, reporter);
+    }
+
+    @Override
+    protected String getName(final Any<?> any) {
+        return User.class.cast(any).getUsername();
+    }
+
+    @Override
+    protected Any<?> getAny(final String key) {
+        try {
+            return userDAO.authFind(key);
+        } catch (Exception e) {
+            LOG.warn("Error retrieving user {}", key, e);
+            return null;
+        }
+    }
+
+    @Override
+    protected AnyTO getAnyTO(final String key) {
+        return userDataBinder.getUserTO(key);
+    }
+
+    @Override
+    protected AnyPatch newPatch(final String key) {
+        UserPatch patch = new UserPatch();
+        patch.setKey(key);
+        return patch;
+    }
+
+    @Override
+    protected WorkflowResult<? extends AnyPatch> update(final AnyPatch patch) {
+        WorkflowResult<Pair<UserPatch, Boolean>> update = uwfAdapter.update((UserPatch) patch);
+        return new WorkflowResult<>(update.getResult().getLeft(), update.getPropByRes(), update.getPerformedTasks());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/9c289134/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java
deleted file mode 100644
index 3e2b5bd..0000000
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java
+++ /dev/null
@@ -1,138 +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.core.provisioning.java.pushpull;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import org.apache.commons.lang3.tuple.Pair;
-import org.apache.syncope.common.lib.patch.AnyPatch;
-import org.apache.syncope.common.lib.patch.AttrPatch;
-import org.apache.syncope.common.lib.patch.GroupPatch;
-import org.apache.syncope.common.lib.to.AnyTO;
-import org.apache.syncope.common.lib.to.PropagationStatus;
-import org.apache.syncope.common.lib.to.GroupTO;
-import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.apache.syncope.common.lib.types.PatchOperation;
-import org.apache.syncope.core.persistence.api.entity.Any;
-import org.apache.syncope.core.persistence.api.entity.AnyUtils;
-import org.apache.syncope.core.provisioning.api.GroupProvisioningManager;
-import org.apache.syncope.core.provisioning.api.ProvisioningManager;
-import org.apache.syncope.core.provisioning.api.WorkflowResult;
-import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
-import org.identityconnectors.framework.common.objects.SyncDelta;
-import org.apache.syncope.core.provisioning.api.pushpull.GroupPullResultHandler;
-import org.springframework.beans.factory.annotation.Autowired;
-
-public class GroupPullResultHandlerImpl extends AbstractPullResultHandler implements GroupPullResultHandler {
-
-    @Autowired
-    private GroupProvisioningManager groupProvisioningManager;
-
-    private final Map<String, String> groupOwnerMap = new HashMap<>();
-
-    @Override
-    public Map<String, String> getGroupOwnerMap() {
-        return this.groupOwnerMap;
-    }
-
-    @Override
-    protected AnyUtils getAnyUtils() {
-        return anyUtilsFactory.getInstance(AnyTypeKind.GROUP);
-    }
-
-    @Override
-    protected String getName(final AnyTO anyTO) {
-        return GroupTO.class.cast(anyTO).getName();
-    }
-
-    @Override
-    protected ProvisioningManager<?, ?> getProvisioningManager() {
-        return groupProvisioningManager;
-    }
-
-    @Override
-    protected Any<?> getAny(final String key) {
-        try {
-            return groupDAO.authFind(key);
-        } catch (Exception e) {
-            LOG.warn("Error retrieving group {}", key, e);
-            return null;
-        }
-    }
-
-    @Override
-    protected AnyTO getAnyTO(final String key) {
-        return groupDataBinder.getGroupTO(key);
-    }
-
-    @Override
-    protected AnyPatch newPatch(final String key) {
-        GroupPatch patch = new GroupPatch();
-        patch.setKey(key);
-        return patch;
-    }
-
-    @Override
-    protected WorkflowResult<? extends AnyPatch> update(final AnyPatch patch) {
-        return gwfAdapter.update((GroupPatch) patch);
-    }
-
-    @Override
-    protected AnyTO doCreate(final AnyTO anyTO, final SyncDelta delta) {
-        GroupTO groupTO = GroupTO.class.cast(anyTO);
-
-        Map.Entry<String, List<PropagationStatus>> created = groupProvisioningManager.create(
-                groupTO,
-                groupOwnerMap,
-                Collections.singleton(profile.getTask().getResource().getKey()),
-                true);
-
-        return getAnyTO(created.getKey());
-    }
-
-    @Override
-    protected AnyPatch doUpdate(
-            final AnyTO before,
-            final AnyPatch anyPatch,
-            final SyncDelta delta,
-            final ProvisioningReport result) {
-
-        GroupPatch groupPatch = GroupPatch.class.cast(anyPatch);
-
-        Pair<GroupPatch, List<PropagationStatus>> updated = groupProvisioningManager.update(
-                groupPatch, Collections.singleton(profile.getTask().getResource().getKey()), true);
-
-        String groupOwner = null;
-        for (AttrPatch attrPatch : groupPatch.getPlainAttrs()) {
-            if (attrPatch.getOperation() == PatchOperation.ADD_REPLACE && attrPatch.getAttrTO() != null
-                    && attrPatch.getAttrTO().getSchema().isEmpty() && !attrPatch.getAttrTO().getValues().isEmpty()) {
-
-                groupOwner = attrPatch.getAttrTO().getValues().get(0);
-            }
-        }
-        if (groupOwner != null) {
-            groupOwnerMap.put(updated.getLeft().getKey(), groupOwner);
-        }
-
-        return anyPatch;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/9c289134/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPushResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPushResultHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPushResultHandlerImpl.java
deleted file mode 100644
index 01a112d..0000000
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPushResultHandlerImpl.java
+++ /dev/null
@@ -1,70 +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.core.provisioning.java.pushpull;
-
-import org.apache.syncope.common.lib.patch.GroupPatch;
-import org.apache.syncope.common.lib.patch.AnyPatch;
-import org.apache.syncope.common.lib.to.AnyTO;
-import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.apache.syncope.core.persistence.api.entity.Any;
-import org.apache.syncope.core.persistence.api.entity.AnyUtils;
-import org.apache.syncope.core.persistence.api.entity.group.Group;
-import org.apache.syncope.core.provisioning.api.WorkflowResult;
-import org.apache.syncope.core.provisioning.api.pushpull.GroupPushResultHandler;
-
-public class GroupPushResultHandlerImpl extends AbstractPushResultHandler implements GroupPushResultHandler {
-
-    @Override
-    protected AnyUtils getAnyUtils() {
-        return anyUtilsFactory.getInstance(AnyTypeKind.GROUP);
-    }
-
-    @Override
-    protected String getName(final Any<?> any) {
-        return Group.class.cast(any).getName();
-    }
-
-    @Override
-    protected Any<?> getAny(final String key) {
-        try {
-            return groupDAO.authFind(key);
-        } catch (Exception e) {
-            LOG.warn("Error retrieving group {}", key, e);
-            return null;
-        }
-    }
-
-    @Override
-    protected AnyTO getAnyTO(final String key) {
-        return groupDataBinder.getGroupTO(key);
-    }
-
-    @Override
-    protected AnyPatch newPatch(final String key) {
-        GroupPatch patch = new GroupPatch();
-        patch.setKey(key);
-        return patch;
-    }
-
-    @Override
-    protected WorkflowResult<? extends AnyPatch> update(final AnyPatch patch) {
-        return gwfAdapter.update((GroupPatch) patch);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/9c289134/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java
index 9c6f486..8bfe089 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java
@@ -49,6 +49,7 @@ import org.apache.syncope.core.persistence.api.entity.task.PullTask;
 import org.apache.syncope.core.provisioning.api.pushpull.AnyObjectPullResultHandler;
 import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
 import org.apache.syncope.core.provisioning.api.pushpull.GroupPullResultHandler;
+import org.apache.syncope.core.provisioning.api.pushpull.RealmPullResultHandler;
 import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullExecutor;
 import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullResultHandler;
 import org.apache.syncope.core.provisioning.api.pushpull.UserPullResultHandler;
@@ -60,25 +61,35 @@ import org.identityconnectors.framework.common.objects.SyncToken;
 public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> implements SyncopePullExecutor {
 
     @Autowired
-    private UserDAO userDAO;
+    protected UserDAO userDAO;
 
     @Autowired
-    private GroupDAO groupDAO;
+    protected GroupDAO groupDAO;
 
     @Autowired
-    private VirSchemaDAO virSchemaDAO;
+    protected VirSchemaDAO virSchemaDAO;
 
     @Autowired
-    private PullUtils pullUtils;
+    protected PullUtils pullUtils;
 
-    private final Map<ObjectClass, SyncToken> latestSyncTokens = new HashMap<>();
+    protected final Map<ObjectClass, SyncToken> latestSyncTokens = new HashMap<>();
+
+    protected ProvisioningProfile<PullTask, PullActions> profile;
+
+    protected RealmPullResultHandler rhandler;
+
+    protected AnyObjectPullResultHandler ahandler;
+
+    protected UserPullResultHandler uhandler;
+
+    protected GroupPullResultHandler ghandler;
 
     @Override
     public void setLatestSyncToken(final ObjectClass objectClass, final SyncToken latestSyncToken) {
         latestSyncTokens.put(objectClass, latestSyncToken);
     }
 
-    private void setGroupOwners(final GroupPullResultHandler ghandler) {
+    protected void setGroupOwners(final GroupPullResultHandler ghandler) {
         ghandler.getGroupOwnerMap().entrySet().stream().map(entry -> {
             Group group = groupDAO.find(entry.getKey());
             if (group == null) {
@@ -114,6 +125,42 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
         });
     }
 
+    protected RealmPullResultHandler buildRealmHandler() {
+        RealmPullResultHandler handler = (RealmPullResultHandler) ApplicationContextProvider.getBeanFactory().
+                createBean(DefaultRealmPullResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
+        handler.setProfile(profile);
+        handler.setPullExecutor(this);
+
+        return handler;
+    }
+
+    protected AnyObjectPullResultHandler buildAnyObjectHandler() {
+        AnyObjectPullResultHandler handler = (AnyObjectPullResultHandler) ApplicationContextProvider.getBeanFactory().
+                createBean(DefaultAnyObjectPullResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
+        handler.setProfile(profile);
+        handler.setPullExecutor(this);
+
+        return handler;
+    }
+
+    protected UserPullResultHandler buildUserHandler() {
+        UserPullResultHandler handler = (UserPullResultHandler) ApplicationContextProvider.getBeanFactory().
+                createBean(DefaultUserPullResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
+        handler.setProfile(profile);
+        handler.setPullExecutor(this);
+
+        return handler;
+    }
+
+    protected GroupPullResultHandler buildGroupHandler() {
+        GroupPullResultHandler handler = (GroupPullResultHandler) ApplicationContextProvider.getBeanFactory().
+                createBean(DefaultGroupPullResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
+        handler.setProfile(profile);
+        handler.setPullExecutor(this);
+
+        return handler;
+    }
+
     @Override
     protected String doExecuteProvisioning(
             final PullTask pullTask,
@@ -135,7 +182,7 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
             }
         });
 
-        ProvisioningProfile<PullTask, PullActions> profile = new ProvisioningProfile<>(connector, pullTask);
+        profile = new ProvisioningProfile<>(connector, pullTask);
         profile.getActions().addAll(actions);
         profile.setDryRun(dryRun);
         profile.setResAct(getPullPolicySpec(pullTask).getConflictResolutionAction());
@@ -148,16 +195,13 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
             }
         }
 
-        // First OrgUnits...
+        // First realms...
         if (pullTask.getResource().getOrgUnit() != null) {
             OrgUnit orgUnit = pullTask.getResource().getOrgUnit();
             OperationOptions options = MappingUtils.buildOperationOptions(
                     MappingUtils.getPullItems(orgUnit.getItems()).iterator());
 
-            SyncopePullResultHandler rhandler = (SyncopePullResultHandler) ApplicationContextProvider.getBeanFactory().
-                    createBean(RealmPullResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
-            rhandler.setProfile(profile);
-            rhandler.setPullExecutor(this);
+            rhandler = buildRealmHandler();
 
             try {
                 switch (pullTask.getPullMode()) {
@@ -202,20 +246,9 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
         }
 
         // ...then provisions for any types
-        AnyObjectPullResultHandler ahandler = (AnyObjectPullResultHandler) ApplicationContextProvider.getBeanFactory().
-                createBean(AnyObjectPullResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
-        ahandler.setProfile(profile);
-        ahandler.setPullExecutor(this);
-
-        UserPullResultHandler uhandler = (UserPullResultHandler) ApplicationContextProvider.getBeanFactory().
-                createBean(UserPullResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
-        uhandler.setProfile(profile);
-        uhandler.setPullExecutor(this);
-
-        GroupPullResultHandler ghandler = (GroupPullResultHandler) ApplicationContextProvider.getBeanFactory().
-                createBean(GroupPullResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
-        ghandler.setProfile(profile);
-        ghandler.setPullExecutor(this);
+        ahandler = buildAnyObjectHandler();
+        uhandler = buildUserHandler();
+        ghandler = buildGroupHandler();
 
         for (Provision provision : pullTask.getResource().getProvisions()) {
             if (provision.getMapping() != null) {
@@ -303,7 +336,7 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
         return result;
     }
 
-    private PullPolicySpec getPullPolicySpec(final ProvisioningTask task) {
+    protected PullPolicySpec getPullPolicySpec(final ProvisioningTask task) {
         PullPolicySpec pullPolicySpec;
 
         if (task instanceof PullTask) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/9c289134/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
index 996ccad..ad3ccfd 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
@@ -45,6 +45,7 @@ import org.apache.syncope.core.provisioning.api.pushpull.AnyObjectPushResultHand
 import org.apache.syncope.core.provisioning.api.pushpull.GroupPushResultHandler;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
 import org.apache.syncope.core.provisioning.api.pushpull.PushActions;
+import org.apache.syncope.core.provisioning.api.pushpull.RealmPushResultHandler;
 import org.apache.syncope.core.provisioning.api.pushpull.SyncopePushResultHandler;
 import org.apache.syncope.core.provisioning.api.pushpull.UserPushResultHandler;
 import org.quartz.JobExecutionException;
@@ -57,27 +58,37 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
      * User DAO.
      */
     @Autowired
-    private UserDAO userDAO;
+    protected UserDAO userDAO;
 
     /**
      * Search DAO.
      */
     @Autowired
-    private AnySearchDAO searchDAO;
+    protected AnySearchDAO searchDAO;
 
     /**
      * Group DAO.
      */
     @Autowired
-    private GroupDAO groupDAO;
+    protected GroupDAO groupDAO;
 
     @Autowired
-    private AnyObjectDAO anyObjectDAO;
+    protected AnyObjectDAO anyObjectDAO;
 
     @Autowired
-    private RealmDAO realmDAO;
+    protected RealmDAO realmDAO;
 
-    private AnyDAO<?> getAnyDAO(final AnyTypeKind anyTypeKind) {
+    protected ProvisioningProfile<PushTask, PushActions> profile;
+
+    protected RealmPushResultHandler rhandler;
+
+    protected AnyObjectPushResultHandler ahandler;
+
+    protected UserPushResultHandler uhandler;
+
+    protected GroupPushResultHandler ghandler;
+
+    protected AnyDAO<?> getAnyDAO(final AnyTypeKind anyTypeKind) {
         AnyDAO<?> result;
         switch (anyTypeKind) {
             case USER:
@@ -96,7 +107,7 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
         return result;
     }
 
-    private void doHandle(
+    protected void doHandle(
             final List<? extends Any<?>> anys,
             final SyncopePushResultHandler handler,
             final ExternalResource resource)
@@ -112,6 +123,38 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
         }
     }
 
+    protected RealmPushResultHandler buildRealmHandler() {
+        RealmPushResultHandler handler = (RealmPushResultHandler) ApplicationContextProvider.getBeanFactory().
+                createBean(DefaultRealmPushResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
+        handler.setProfile(profile);
+
+        return handler;
+    }
+
+    protected AnyObjectPushResultHandler buildAnyObjectHandler() {
+        AnyObjectPushResultHandler handler = (AnyObjectPushResultHandler) ApplicationContextProvider.getBeanFactory().
+                createBean(DefaultAnyObjectPushResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
+        handler.setProfile(profile);
+
+        return handler;
+    }
+
+    protected UserPushResultHandler buildUserHandler() {
+        UserPushResultHandler handler = (UserPushResultHandler) ApplicationContextProvider.getBeanFactory().
+                createBean(DefaultUserPushResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
+        handler.setProfile(profile);
+
+        return handler;
+    }
+
+    protected GroupPushResultHandler buildGroupHandler() {
+        GroupPushResultHandler handler = (GroupPushResultHandler) ApplicationContextProvider.getBeanFactory().
+                createBean(DefaultGroupPushResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
+        handler.setProfile(profile);
+
+        return handler;
+    }
+
     @Override
     protected String doExecuteProvisioning(
             final PushTask pushTask,
@@ -133,7 +176,7 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
             }
         });
 
-        ProvisioningProfile<PushTask, PushActions> profile = new ProvisioningProfile<>(connector, pushTask);
+        profile = new ProvisioningProfile<>(connector, pushTask);
         profile.getActions().addAll(actions);
         profile.setDryRun(dryRun);
         profile.setResAct(null);
@@ -144,11 +187,9 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
             }
         }
 
-        // First OrgUnits...
+        // First realms...
         if (pushTask.getResource().getOrgUnit() != null) {
-            SyncopePushResultHandler rhandler = (SyncopePushResultHandler) ApplicationContextProvider.getBeanFactory().
-                    createBean(RealmPushResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
-            rhandler.setProfile(profile);
+            rhandler = buildRealmHandler();
 
             for (Realm realm : realmDAO.findDescendants(profile.getTask().getSourceRealm())) {
                 // Never push the root realm
@@ -164,19 +205,9 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
         }
 
         // ...then provisions for any types
-        AnyObjectPushResultHandler ahandler = (AnyObjectPushResultHandler) ApplicationContextProvider.getBeanFactory().
-                createBean(AnyObjectPushResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
-        ahandler.setProfile(profile);
-
-        UserPushResultHandler uhandler =
-                (UserPushResultHandler) ApplicationContextProvider.getBeanFactory().
-                        createBean(UserPushResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
-        uhandler.setProfile(profile);
-
-        GroupPushResultHandler ghandler =
-                (GroupPushResultHandler) ApplicationContextProvider.getBeanFactory().
-                        createBean(GroupPushResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
-        ghandler.setProfile(profile);
+        ahandler = buildAnyObjectHandler();
+        uhandler = buildUserHandler();
+        ghandler = buildGroupHandler();
 
         for (Provision provision : pushTask.getResource().getProvisions()) {
             if (provision.getMapping() != null) {