[SYNCOPE-663] Merge from 1_2_X

Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/5d45494f
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/5d45494f
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/5d45494f

Branch: refs/heads/master
Commit: 5d45494f09042eab9097fd45b3ceea5a4fcaeb07
Parents: 65fbb52 dda37dc
Author: Marco Di Sabatino Di Diodoro <[email protected]>
Authored: Thu Apr 30 18:04:50 2015 +0200
Committer: Marco Di Sabatino Di Diodoro <[email protected]>
Committed: Thu Apr 30 18:04:50 2015 +0200

----------------------------------------------------------------------
 .../java/sync/AbstractPushResultHandler.java             |  3 +++
 .../java/sync/AbstractSyncResultHandler.java             | 11 +++++++++++
 2 files changed, 14 insertions(+)
----------------------------------------------------------------------


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

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

Reply via email to