protected ConnectorObject getRemoteObject(
      final String connObjectKey, final ObjectClass objectClass) {
    ConnectorObject obj = null;
    try {
      Uid uid = new Uid(connObjectKey);

      obj =
          profile
              .getConnector()
              .getObject(
                  objectClass,
                  uid,
                  MappingUtils.buildOperationOptions(IteratorUtils.<MappingItem>emptyIterator()));
    } catch (TimeoutException toe) {
      LOG.debug("Request timeout", toe);
      throw toe;
    } catch (RuntimeException ignore) {
      LOG.debug("While resolving {}", connObjectKey, ignore);
    }

    return obj;
  }
  protected final void doHandle(final Any<?> any) throws JobExecutionException {
    AnyUtils anyUtils = anyUtilsFactory.getInstance(any);

    ProvisioningReport result = new ProvisioningReport();
    profile.getResults().add(result);

    result.setKey(any.getKey());
    result.setAnyType(any.getType().getKey());
    result.setName(getName(any));

    Boolean enabled =
        any instanceof User && profile.getTask().isSyncStatus()
            ? ((User) any).isSuspended() ? Boolean.FALSE : Boolean.TRUE
            : null;

    LOG.debug(
        "Propagating {} with key {} towards {}",
        anyUtils.getAnyTypeKind(),
        any.getKey(),
        profile.getTask().getResource());

    Object output = null;
    Result resultStatus = null;
    String operation = null;

    // Try to read remote object BEFORE any actual operation
    Provision provision = profile.getTask().getResource().getProvision(any.getType());
    String connObjecKey = mappingUtils.getConnObjectKeyValue(any, provision);

    ConnectorObject beforeObj = getRemoteObject(connObjecKey, provision.getObjectClass());

    Boolean status = profile.getTask().isSyncStatus() ? enabled : null;

    if (profile.isDryRun()) {
      if (beforeObj == null) {
        result.setOperation(getResourceOperation(profile.getTask().getUnmatchingRule()));
      } else {
        result.setOperation(getResourceOperation(profile.getTask().getMatchingRule()));
      }
      result.setStatus(ProvisioningReport.Status.SUCCESS);
    } else {
      try {
        if (beforeObj == null) {
          operation = UnmatchingRule.toEventName(profile.getTask().getUnmatchingRule());
          result.setOperation(getResourceOperation(profile.getTask().getUnmatchingRule()));

          switch (profile.getTask().getUnmatchingRule()) {
            case ASSIGN:
              for (PushActions action : profile.getActions()) {
                action.beforeAssign(this.getProfile(), any);
              }

              if (!profile.getTask().isPerformCreate()) {
                LOG.debug("PushTask not configured for create");
              } else {
                assign(any, status);
              }

              break;

            case PROVISION:
              for (PushActions action : profile.getActions()) {
                action.beforeProvision(this.getProfile(), any);
              }

              if (!profile.getTask().isPerformCreate()) {
                LOG.debug("PushTask not configured for create");
              } else {
                provision(any, status);
              }

              break;

            case UNLINK:
              for (PushActions action : profile.getActions()) {
                action.beforeUnlink(this.getProfile(), any);
              }

              if (!profile.getTask().isPerformUpdate()) {
                LOG.debug("PushTask not configured for update");
              } else {
                link(any, true);
              }

              break;

            case IGNORE:
              LOG.debug("Ignored any: {}", any);
              break;
            default:
              // do nothing
          }
        } else {
          operation = MatchingRule.toEventName(profile.getTask().getMatchingRule());
          result.setOperation(getResourceOperation(profile.getTask().getMatchingRule()));

          switch (profile.getTask().getMatchingRule()) {
            case UPDATE:
              for (PushActions action : profile.getActions()) {
                action.beforeUpdate(this.getProfile(), any);
              }
              if (!profile.getTask().isPerformUpdate()) {
                LOG.debug("PushTask not configured for update");
              } else {
                update(any, status);
              }

              break;

            case DEPROVISION:
              for (PushActions action : profile.getActions()) {
                action.beforeDeprovision(this.getProfile(), any);
              }

              if (!profile.getTask().isPerformDelete()) {
                LOG.debug("PushTask not configured for delete");
              } else {
                deprovision(any);
              }

              break;

            case UNASSIGN:
              for (PushActions action : profile.getActions()) {
                action.beforeUnassign(this.getProfile(), any);
              }

              if (!profile.getTask().isPerformDelete()) {
                LOG.debug("PushTask not configured for delete");
              } else {
                unassign(any);
              }

              break;

            case LINK:
              for (PushActions action : profile.getActions()) {
                action.beforeLink(this.getProfile(), any);
              }

              if (!profile.getTask().isPerformUpdate()) {
                LOG.debug("PushTask not configured for update");
              } else {
                link(any, false);
              }

              break;

            case UNLINK:
              for (PushActions action : profile.getActions()) {
                action.beforeUnlink(this.getProfile(), any);
              }

              if (!profile.getTask().isPerformUpdate()) {
                LOG.debug("PushTask not configured for update");
              } else {
                link(any, true);
              }

              break;

            case IGNORE:
              LOG.debug("Ignored any: {}", any);
              break;
            default:
              // do nothing
          }
        }

        for (PushActions action : profile.getActions()) {
          action.after(this.getProfile(), any, result);
        }

        result.setStatus(ProvisioningReport.Status.SUCCESS);
        resultStatus = AuditElements.Result.SUCCESS;
        output = getRemoteObject(connObjecKey, provision.getObjectClass());
      } 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 {}", any, profile.getTask().getResource(), e);

        for (PushActions action : profile.getActions()) {
          action.onError(this.getProfile(), any, result, e);
        }

        throw new JobExecutionException(e);
      } finally {
        notificationManager.createTasks(
            AuditElements.EventCategoryType.PUSH,
            any.getType().getKind().name().toLowerCase(),
            profile.getTask().getResource().getKey(),
            operation,
            resultStatus,
            beforeObj,
            output,
            any);
        auditManager.audit(
            AuditElements.EventCategoryType.PUSH,
            any.getType().getKind().name().toLowerCase(),
            profile.getTask().getResource().getKey(),
            operation,
            resultStatus,
            connObjectUtils.getConnObjectTO(beforeObj),
            output instanceof ConnectorObject
                ? connObjectUtils.getConnObjectTO((ConnectorObject) output)
                : output,
            any);
      }
    }
  }