org.apache.syncope.core.sync.impl.AbstractSubjectSyncResultHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.syncope.core.sync.impl.AbstractSubjectSyncResultHandler.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.syncope.core.sync.impl;

import org.apache.syncope.core.sync.SyncUtilities;

import static org.apache.syncope.core.sync.impl.AbstractSyncopeResultHandler.LOG;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.syncope.common.mod.AbstractSubjectMod;
import org.apache.syncope.common.to.AbstractSubjectTO;
import org.apache.syncope.common.types.AuditElements;
import org.apache.syncope.common.types.AuditElements.Result;
import org.apache.syncope.common.types.ResourceOperation;
import org.apache.syncope.core.persistence.beans.SyncTask;
import org.apache.syncope.core.persistence.dao.NotFoundException;
import org.apache.syncope.core.persistence.dao.UserDAO;
import org.apache.syncope.core.propagation.PropagationException;
import org.apache.syncope.core.rest.controller.UnauthorizedRoleException;
import org.apache.syncope.core.rest.data.AttributableTransformer;
import org.apache.syncope.core.sync.SyncActions;
import org.apache.syncope.core.sync.SyncResult;
import org.apache.syncope.core.util.AttributableUtil;
import org.identityconnectors.framework.common.objects.SyncDelta;
import org.identityconnectors.framework.common.objects.SyncDeltaType;
import org.identityconnectors.framework.common.objects.SyncResultsHandler;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;

public abstract class AbstractSubjectSyncResultHandler extends AbstractSyncopeResultHandler<SyncTask, SyncActions>
        implements SyncResultsHandler {

    @Autowired
    protected SyncUtilities syncUtilities;

    @Autowired
    protected AttributableTransformer attrTransformer;

    @Autowired
    protected UserDAO userDAO;

    protected abstract AttributableUtil getAttributableUtil();

    protected abstract String getName(AbstractSubjectTO subjectTO);

    protected abstract AbstractSubjectTO getSubjectTO(long id);

    protected abstract AbstractSubjectMod getSubjectMod(AbstractSubjectTO subjectTO, SyncDelta delta);

    protected abstract AbstractSubjectTO create(AbstractSubjectTO subjectTO, SyncDelta _delta, SyncResult result);

    protected abstract AbstractSubjectTO link(AbstractSubjectTO before, SyncResult result, boolean unlink)
            throws Exception;

    protected abstract AbstractSubjectTO update(AbstractSubjectTO before, AbstractSubjectMod subjectMod,
            SyncDelta delta, SyncResult result) throws Exception;

    protected abstract void deprovision(Long id, boolean unlink) throws Exception;

    protected abstract void delete(Long id);

    @Override
    public boolean handle(final SyncDelta delta) {
        try {
            if (profile.getResults() == null) {
                profile.setResults(new ArrayList<SyncResult>());
            }

            doHandle(delta, profile.getResults());
            return true;
        } catch (JobExecutionException e) {
            LOG.error("Synchronization failed", e);
            return false;
        }
    }

    protected List<SyncResult> assign(final SyncDelta delta, final AttributableUtil attrUtil)
            throws JobExecutionException {
        if (!profile.getSyncTask().isPerformCreate()) {
            LOG.debug("SyncTask not configured for create");
            return Collections.<SyncResult>emptyList();
        }

        final AbstractSubjectTO subjectTO = connObjectUtil.getSubjectTO(delta.getObject(), profile.getSyncTask(),
                attrUtil);

        subjectTO.getResources().add(profile.getSyncTask().getResource().getName());

        final SyncResult result = new SyncResult();
        result.setOperation(ResourceOperation.CREATE);
        result.setSubjectType(attrUtil.getType());
        result.setStatus(SyncResult.Status.SUCCESS);

        // Attributable transformation (if configured)
        AbstractSubjectTO transformed = attrTransformer.transform(subjectTO);
        LOG.debug("Transformed: {}", transformed);

        result.setName(getName(transformed));

        if (profile.isDryRun()) {
            result.setId(0L);
        } else {
            SyncDelta _delta = delta;
            for (SyncActions action : profile.getActions()) {
                _delta = action.beforeAssign(this.getProfile(), _delta, transformed);
            }

            create(transformed, _delta, attrUtil, "assign", result);
        }

        return Collections.singletonList(result);
    }

    protected List<SyncResult> create(final SyncDelta delta, final AttributableUtil attrUtil)
            throws JobExecutionException {
        if (!profile.getSyncTask().isPerformCreate()) {
            LOG.debug("SyncTask not configured for create");
            return Collections.<SyncResult>emptyList();
        }

        final AbstractSubjectTO subjectTO = connObjectUtil.getSubjectTO(delta.getObject(), profile.getSyncTask(),
                attrUtil);

        // Attributable transformation (if configured)
        AbstractSubjectTO transformed = attrTransformer.transform(subjectTO);
        LOG.debug("Transformed: {}", transformed);

        final SyncResult result = new SyncResult();
        result.setOperation(ResourceOperation.CREATE);
        result.setSubjectType(attrUtil.getType());
        result.setStatus(SyncResult.Status.SUCCESS);

        result.setName(getName(transformed));

        if (profile.isDryRun()) {
            result.setId(0L);
        } else {
            SyncDelta _delta = delta;
            for (SyncActions action : profile.getActions()) {
                _delta = action.beforeProvision(this.getProfile(), _delta, transformed);
            }

            create(transformed, _delta, attrUtil, "provision", result);
        }

        return Collections.<SyncResult>singletonList(result);
    }

    private void create(final AbstractSubjectTO subjectTO, final SyncDelta delta, final AttributableUtil attrUtil,
            final String operation, final SyncResult result) throws JobExecutionException {

        Object output;
        Result resultStatus;

        try {
            AbstractSubjectTO actual = create(subjectTO, delta, result);
            result.setName(getName(actual));
            output = actual;
            resultStatus = Result.SUCCESS;

            for (SyncActions action : profile.getActions()) {
                action.after(this.getProfile(), delta, actual, result);
            }
        } catch (PropagationException e) {
            // A propagation failure doesn't imply a synchronization failure.
            // The propagation exception status will be reported into the propagation task execution.
            LOG.error("Could not propagate {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
            output = e;
            resultStatus = Result.FAILURE;
        } catch (Exception e) {
            result.setStatus(SyncResult.Status.FAILURE);
            result.setMessage(ExceptionUtils.getRootCauseMessage(e));
            LOG.error("Could not create {} {} ", attrUtil.getType(), delta.getUid().getUidValue(), e);
            output = e;
            resultStatus = Result.FAILURE;
        }

        audit(operation, resultStatus, null, output, delta);
    }

    protected List<SyncResult> update(SyncDelta delta, final List<Long> subjects, final AttributableUtil attrUtil)
            throws JobExecutionException {

        if (!profile.getSyncTask().isPerformUpdate()) {
            LOG.debug("SyncTask not configured for update");
            return Collections.<SyncResult>emptyList();
        }

        LOG.debug("About to update {}", subjects);

        List<SyncResult> updResults = new ArrayList<SyncResult>();

        for (Long id : subjects) {
            LOG.debug("About to update {}", id);

            Object output;
            AbstractSubjectTO before = null;
            Result resultStatus;

            final SyncResult result = new SyncResult();
            result.setOperation(ResourceOperation.UPDATE);
            result.setSubjectType(attrUtil.getType());
            result.setStatus(SyncResult.Status.SUCCESS);
            result.setId(id);

            before = getSubjectTO(id);

            if (before == null) {
                result.setStatus(SyncResult.Status.FAILURE);
                result.setMessage(String.format("Subject '%s(%d)' not found", attrUtil.getType().name(), id));
            } else {
                result.setName(getName(before));
            }

            if (!profile.isDryRun()) {
                if (before == null) {
                    resultStatus = Result.FAILURE;
                    output = null;
                } else {
                    try {
                        final AbstractSubjectMod attributableMod = getSubjectMod(before, delta);

                        // Attribute value transformation (if configured)
                        final AbstractSubjectMod actual = attrTransformer.transform(attributableMod);
                        LOG.debug("Transformed: {}", actual);

                        for (SyncActions action : profile.getActions()) {
                            delta = action.beforeUpdate(this.getProfile(), delta, before, attributableMod);
                        }

                        final AbstractSubjectTO updated = update(before, attributableMod, delta, result);

                        for (SyncActions action : profile.getActions()) {
                            action.after(this.getProfile(), delta, updated, result);
                        }

                        output = updated;
                        resultStatus = Result.SUCCESS;
                        result.setName(getName(updated));
                        LOG.debug("{} {} successfully updated", attrUtil.getType(), id);
                    } catch (PropagationException e) {
                        // A propagation failure doesn't imply a synchronization failure.
                        // The propagation exception status will be reported into the propagation task execution.
                        LOG.error("Could not propagate {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
                        output = e;
                        resultStatus = Result.FAILURE;
                    } catch (Exception e) {
                        result.setStatus(SyncResult.Status.FAILURE);
                        result.setMessage(ExceptionUtils.getRootCauseMessage(e));
                        LOG.error("Could not update {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
                        output = e;
                        resultStatus = Result.FAILURE;
                    }
                }
                audit("update", resultStatus, before, output, delta);
            }
            updResults.add(result);
        }
        return updResults;
    }

    protected List<SyncResult> deprovision(SyncDelta delta, final List<Long> subjects,
            final AttributableUtil attrUtil, final boolean unlink) throws JobExecutionException {

        if (!profile.getSyncTask().isPerformUpdate()) {
            LOG.debug("SyncTask not configured for update");
            return Collections.<SyncResult>emptyList();
        }

        LOG.debug("About to update {}", subjects);

        final List<SyncResult> updResults = new ArrayList<SyncResult>();

        for (Long id : subjects) {
            LOG.debug("About to unassign resource {}", id);

            Object output;
            Result resultStatus;

            final SyncResult result = new SyncResult();
            result.setOperation(ResourceOperation.DELETE);
            result.setSubjectType(attrUtil.getType());
            result.setStatus(SyncResult.Status.SUCCESS);
            result.setId(id);

            final AbstractSubjectTO before = getSubjectTO(id);

            if (before == null) {
                result.setStatus(SyncResult.Status.FAILURE);
                result.setMessage(String.format("Subject '%s(%d)' not found", attrUtil.getType().name(), id));
            }

            if (!profile.isDryRun()) {
                if (before == null) {
                    resultStatus = Result.FAILURE;
                    output = null;
                } else {
                    result.setName(getName(before));

                    try {
                        if (unlink) {
                            for (SyncActions action : profile.getActions()) {
                                action.beforeUnassign(this.getProfile(), delta, before);
                            }
                        } else {
                            for (SyncActions action : profile.getActions()) {
                                action.beforeDeprovision(this.getProfile(), delta, before);
                            }
                        }

                        deprovision(id, unlink);
                        output = getSubjectTO(id);

                        for (SyncActions action : profile.getActions()) {
                            action.after(this.getProfile(), delta, AbstractSubjectTO.class.cast(output), result);
                        }

                        resultStatus = Result.SUCCESS;
                        LOG.debug("{} {} successfully updated", attrUtil.getType(), id);
                    } catch (PropagationException e) {
                        // A propagation failure doesn't imply a synchronization failure.
                        // The propagation exception status will be reported into the propagation task execution.
                        LOG.error("Could not propagate {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
                        output = e;
                        resultStatus = Result.FAILURE;
                    } catch (Exception e) {
                        result.setStatus(SyncResult.Status.FAILURE);
                        result.setMessage(ExceptionUtils.getRootCauseMessage(e));
                        LOG.error("Could not update {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
                        output = e;
                        resultStatus = Result.FAILURE;
                    }
                }
                audit(unlink ? "unassign" : "deprovision", resultStatus, before, output, delta);
            }
            updResults.add(result);
        }

        return updResults;
    }

    protected List<SyncResult> link(SyncDelta delta, final List<Long> subjects, final AttributableUtil attrUtil,
            final boolean unlink) throws JobExecutionException {

        if (!profile.getSyncTask().isPerformUpdate()) {
            LOG.debug("SyncTask not configured for update");
            return Collections.<SyncResult>emptyList();
        }

        LOG.debug("About to update {}", subjects);

        final List<SyncResult> updResults = new ArrayList<SyncResult>();

        for (Long id : subjects) {
            LOG.debug("About to unassign resource {}", id);

            Object output;
            Result resultStatus;

            final SyncResult result = new SyncResult();
            result.setOperation(ResourceOperation.NONE);
            result.setSubjectType(attrUtil.getType());
            result.setStatus(SyncResult.Status.SUCCESS);
            result.setId(id);

            final AbstractSubjectTO before = getSubjectTO(id);

            if (before == null) {
                result.setStatus(SyncResult.Status.FAILURE);
                result.setMessage(String.format("Subject '%s(%d)' not found", attrUtil.getType().name(), id));
            }

            if (!profile.isDryRun()) {
                if (before == null) {
                    resultStatus = Result.FAILURE;
                    output = null;
                } else {
                    result.setName(getName(before));

                    try {
                        if (unlink) {
                            for (SyncActions action : profile.getActions()) {
                                action.beforeUnlink(this.getProfile(), delta, before);
                            }
                        } else {
                            for (SyncActions action : profile.getActions()) {
                                action.beforeLink(this.getProfile(), delta, before);
                            }
                        }

                        output = link(before, result, unlink);

                        for (SyncActions action : profile.getActions()) {
                            action.after(this.getProfile(), delta, AbstractSubjectTO.class.cast(output), result);
                        }

                        resultStatus = Result.SUCCESS;
                        LOG.debug("{} {} successfully updated", attrUtil.getType(), id);
                    } catch (PropagationException e) {
                        // A propagation failure doesn't imply a synchronization failure.
                        // The propagation exception status will be reported into the propagation task execution.
                        LOG.error("Could not propagate {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
                        output = e;
                        resultStatus = Result.FAILURE;
                    } catch (Exception e) {
                        result.setStatus(SyncResult.Status.FAILURE);
                        result.setMessage(ExceptionUtils.getRootCauseMessage(e));
                        LOG.error("Could not update {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
                        output = e;
                        resultStatus = Result.FAILURE;
                    }
                }
                audit(unlink ? "unlink" : "link", resultStatus, before, output, delta);
            }
            updResults.add(result);
        }

        return updResults;
    }

    protected List<SyncResult> delete(SyncDelta delta, final List<Long> subjects, final AttributableUtil attrUtil)
            throws JobExecutionException {

        if (!profile.getSyncTask().isPerformDelete()) {
            LOG.debug("SyncTask not configured for delete");
            return Collections.<SyncResult>emptyList();
        }

        LOG.debug("About to delete {}", subjects);

        List<SyncResult> delResults = new ArrayList<SyncResult>();

        for (Long id : subjects) {
            Object output;
            Result resultStatus = Result.FAILURE;

            AbstractSubjectTO before = null;
            final SyncResult result = new SyncResult();

            try {
                before = getSubjectTO(id);

                result.setId(id);
                result.setName(getName(before));
                result.setOperation(ResourceOperation.DELETE);
                result.setSubjectType(attrUtil.getType());
                result.setStatus(SyncResult.Status.SUCCESS);

                if (!profile.isDryRun()) {
                    for (SyncActions action : profile.getActions()) {
                        delta = action.beforeDelete(this.getProfile(), delta, before);
                    }

                    try {
                        delete(id);
                        output = null;
                        resultStatus = Result.SUCCESS;
                    } catch (Exception e) {
                        result.setStatus(SyncResult.Status.FAILURE);
                        result.setMessage(ExceptionUtils.getRootCauseMessage(e));
                        LOG.error("Could not delete {} {}", attrUtil.getType(), id, e);
                        output = e;
                    }

                    for (SyncActions action : profile.getActions()) {
                        action.after(this.getProfile(), delta, before, result);
                    }

                    audit("delete", resultStatus, before, output, delta);
                }

                delResults.add(result);

            } catch (NotFoundException e) {
                LOG.error("Could not find {} {}", attrUtil.getType(), id, e);
            } catch (UnauthorizedRoleException e) {
                LOG.error("Not allowed to read {} {}", attrUtil.getType(), id, e);
            } catch (Exception e) {
                LOG.error("Could not delete {} {}", attrUtil.getType(), id, e);
            }
        }

        return delResults;
    }

    /**
     * Look into SyncDelta and take necessary profile.getActions() (create / update / delete) on user(s)/role(s).
     *
     * @param delta returned by the underlying profile.getConnector()
     * @throws JobExecutionException in case of synchronization failure.
     */
    protected final void doHandle(final SyncDelta delta, final Collection<SyncResult> syncResults)
            throws JobExecutionException {

        final AttributableUtil attrUtil = getAttributableUtil();

        LOG.debug("Process {} for {} as {}", delta.getDeltaType(), delta.getUid().getUidValue(),
                delta.getObject().getObjectClass());

        final String uid = delta.getPreviousUid() == null ? delta.getUid().getUidValue()
                : delta.getPreviousUid().getUidValue();

        try {
            List<Long> subjectIds = syncUtilities.findExisting(uid, delta.getObject(),
                    profile.getSyncTask().getResource(), attrUtil);

            if (subjectIds.size() > 1) {
                switch (profile.getResAct()) {
                case IGNORE:
                    throw new IllegalStateException("More than one match " + subjectIds);

                case FIRSTMATCH:
                    subjectIds = subjectIds.subList(0, 1);
                    break;

                case LASTMATCH:
                    subjectIds = subjectIds.subList(subjectIds.size() - 1, subjectIds.size());
                    break;

                default:
                    // keep subjectIds as is
                }
            }

            if (SyncDeltaType.CREATE_OR_UPDATE == delta.getDeltaType()) {
                if (subjectIds.isEmpty()) {
                    switch (profile.getSyncTask().getUnmatchingRule()) {
                    case ASSIGN:
                        profile.getResults().addAll(assign(delta, attrUtil));
                        break;
                    case PROVISION:
                        profile.getResults().addAll(create(delta, attrUtil));
                        break;
                    default:
                        // do nothing
                    }
                } else {
                    switch (profile.getSyncTask().getMatchingRule()) {
                    case UPDATE:
                        profile.getResults().addAll(update(delta, subjectIds, attrUtil));
                        break;
                    case DEPROVISION:
                        profile.getResults().addAll(deprovision(delta, subjectIds, attrUtil, false));
                        break;
                    case UNASSIGN:
                        profile.getResults().addAll(deprovision(delta, subjectIds, attrUtil, true));
                        break;
                    case LINK:
                        profile.getResults().addAll(link(delta, subjectIds, attrUtil, false));
                        break;
                    case UNLINK:
                        profile.getResults().addAll(link(delta, subjectIds, attrUtil, true));
                        break;
                    default:
                        // do nothing
                    }
                }
            } else if (SyncDeltaType.DELETE == delta.getDeltaType()) {
                if (subjectIds.isEmpty()) {
                    LOG.debug("No match found for deletion");
                } else {
                    profile.getResults().addAll(delete(delta, subjectIds, attrUtil));
                }
            }
        } catch (IllegalStateException e) {
            LOG.warn(e.getMessage());
        }
    }

    private void audit(final String event, final Result result, final Object before, final Object output,
            final Object... input) {

        notificationManager.createTasks(AuditElements.EventCategoryType.SYNCHRONIZATION,
                getAttributableUtil().getType().name().toLowerCase(), profile.getSyncTask().getResource().getName(),
                event, result, before, output, input);

        auditManager.audit(AuditElements.EventCategoryType.SYNCHRONIZATION,
                getAttributableUtil().getType().name().toLowerCase(), profile.getSyncTask().getResource().getName(),
                event, result, before, output, input);
    }
}