http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderImpl.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderImpl.java index 42de46f..7771f06 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderImpl.java @@ -65,7 +65,7 @@ public class RealmDataBinderImpl implements RealmDataBinder { private void setTemplates(final RealmTO realmTO, final Realm realm) { // validate JEXL expressions from templates and proceed if fine - templateUtils.check(realmTO.getTemplates(), ClientExceptionType.InvalidSyncTask); + templateUtils.check(realmTO.getTemplates(), ClientExceptionType.InvalidPullTask); for (Map.Entry<String, AnyTO> entry : realmTO.getTemplates().entrySet()) { AnyType type = anyTypeDAO.find(entry.getKey()); if (type == null) {
http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java index 97aa6f2..b4aa8b2 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java @@ -41,7 +41,6 @@ import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource; import org.apache.syncope.core.persistence.api.entity.resource.Mapping; import org.apache.syncope.core.persistence.api.entity.resource.MappingItem; import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy; -import org.apache.syncope.core.persistence.api.entity.policy.SyncPolicy; import org.apache.syncope.core.provisioning.java.jexl.JexlUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,6 +53,7 @@ import org.apache.syncope.core.persistence.api.entity.resource.Provision; import org.identityconnectors.framework.common.objects.ObjectClass; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy; @Component public class ResourceDataBinderImpl implements ResourceDataBinder { @@ -178,7 +178,7 @@ public class ResourceDataBinderImpl implements ResourceDataBinder { resource.setCreateTraceLevel(resourceTO.getCreateTraceLevel()); resource.setUpdateTraceLevel(resourceTO.getUpdateTraceLevel()); resource.setDeleteTraceLevel(resourceTO.getDeleteTraceLevel()); - resource.setSyncTraceLevel(resourceTO.getSyncTraceLevel()); + resource.setPullTraceLevel(resourceTO.getPullTraceLevel()); resource.setPasswordPolicy(resourceTO.getPasswordPolicy() == null ? null : (PasswordPolicy) policyDAO.find(resourceTO.getPasswordPolicy())); @@ -186,8 +186,8 @@ public class ResourceDataBinderImpl implements ResourceDataBinder { resource.setAccountPolicy(resourceTO.getAccountPolicy() == null ? null : (AccountPolicy) policyDAO.find(resourceTO.getAccountPolicy())); - resource.setSyncPolicy(resourceTO.getSyncPolicy() == null - ? null : (SyncPolicy) policyDAO.find(resourceTO.getSyncPolicy())); + resource.setPullPolicy(resourceTO.getPullPolicy() == null + ? null : (PullPolicy) policyDAO.find(resourceTO.getPullPolicy())); resource.setConfOverride(new HashSet<>(resourceTO.getConfOverride())); @@ -340,7 +340,7 @@ public class ResourceDataBinderImpl implements ResourceDataBinder { resourceTO.setCreateTraceLevel(resource.getCreateTraceLevel()); resourceTO.setUpdateTraceLevel(resource.getUpdateTraceLevel()); resourceTO.setDeleteTraceLevel(resource.getDeleteTraceLevel()); - resourceTO.setSyncTraceLevel(resource.getSyncTraceLevel()); + resourceTO.setPullTraceLevel(resource.getPullTraceLevel()); resourceTO.setPasswordPolicy(resource.getPasswordPolicy() == null ? null : resource.getPasswordPolicy().getKey()); @@ -348,8 +348,8 @@ public class ResourceDataBinderImpl implements ResourceDataBinder { resourceTO.setAccountPolicy(resource.getAccountPolicy() == null ? null : resource.getAccountPolicy().getKey()); - resourceTO.setSyncPolicy(resource.getSyncPolicy() == null - ? null : resource.getSyncPolicy().getKey()); + resourceTO.setPullPolicy(resource.getPullPolicy() == null + ? null : resource.getPullPolicy().getKey()); resourceTO.getConfOverride().addAll(resource.getConfOverride()); http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java index e262521..7679ce0 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java @@ -30,7 +30,7 @@ import org.apache.syncope.common.lib.to.AnyTO; import org.apache.syncope.common.lib.to.PropagationTaskTO; import org.apache.syncope.common.lib.to.PushTaskTO; import org.apache.syncope.common.lib.to.SchedTaskTO; -import org.apache.syncope.common.lib.to.SyncTaskTO; +import org.apache.syncope.common.lib.to.PullTaskTO; import org.apache.syncope.common.lib.to.ExecTO; import org.apache.syncope.common.lib.types.ClientExceptionType; import org.apache.syncope.common.lib.types.JobType; @@ -46,7 +46,6 @@ import org.apache.syncope.core.persistence.api.entity.task.PropagationTask; import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask; import org.apache.syncope.core.persistence.api.entity.task.PushTask; import org.apache.syncope.core.persistence.api.entity.task.SchedTask; -import org.apache.syncope.core.persistence.api.entity.task.SyncTask; import org.apache.syncope.core.persistence.api.entity.task.Task; import org.apache.syncope.core.persistence.api.entity.task.TaskExec; import org.apache.syncope.core.persistence.api.entity.task.TaskUtils; @@ -58,9 +57,10 @@ import org.apache.syncope.core.persistence.api.entity.AnyType; import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource; import org.apache.syncope.core.persistence.api.entity.AnyTemplate; -import org.apache.syncope.core.persistence.api.entity.task.AnyTemplateSyncTask; -import org.apache.syncope.core.provisioning.java.syncpull.PushJobDelegate; -import org.apache.syncope.core.provisioning.java.syncpull.SyncJobDelegate; +import org.apache.syncope.core.persistence.api.entity.task.AnyTemplatePullTask; +import org.apache.syncope.core.persistence.api.entity.task.PullTask; +import org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate; +import org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; @@ -142,46 +142,46 @@ public class TaskDataBinderImpl implements TaskDataBinder { return pushTaskTO.getFilters().containsKey(anyFilter.getAnyType().getKey()); } }); - } else if (task instanceof SyncTask && taskTO instanceof SyncTaskTO) { - SyncTask syncTask = (SyncTask) task; - final SyncTaskTO syncTaskTO = (SyncTaskTO) taskTO; + } else if (task instanceof PullTask && taskTO instanceof PullTaskTO) { + PullTask pullTask = (PullTask) task; + final PullTaskTO pullTaskTO = (PullTaskTO) taskTO; - syncTask.setSyncMode(syncTaskTO.getSyncMode()); - syncTask.setReconciliationFilterBuilderClassName(syncTaskTO.getReconciliationFilterBuilderClassName()); + pullTask.setPullMode(pullTaskTO.getPullMode()); + pullTask.setReconciliationFilterBuilderClassName(pullTaskTO.getReconciliationFilterBuilderClassName()); - syncTask.setDestinationRealm(realmDAO.find(syncTaskTO.getDestinationRealm())); + pullTask.setDestinationRealm(realmDAO.find(pullTaskTO.getDestinationRealm())); - syncTask.setJobDelegateClassName(SyncJobDelegate.class.getName()); + pullTask.setJobDelegateClassName(PullJobDelegate.class.getName()); - syncTask.setMatchingRule(syncTaskTO.getMatchingRule() == null - ? MatchingRule.UPDATE : syncTaskTO.getMatchingRule()); - syncTask.setUnmatchingRule(syncTaskTO.getUnmatchingRule() == null - ? UnmatchingRule.PROVISION : syncTaskTO.getUnmatchingRule()); + pullTask.setMatchingRule(pullTaskTO.getMatchingRule() == null + ? MatchingRule.UPDATE : pullTaskTO.getMatchingRule()); + pullTask.setUnmatchingRule(pullTaskTO.getUnmatchingRule() == null + ? UnmatchingRule.PROVISION : pullTaskTO.getUnmatchingRule()); // validate JEXL expressions from templates and proceed if fine - templateUtils.check(syncTaskTO.getTemplates(), ClientExceptionType.InvalidSyncTask); - for (Map.Entry<String, AnyTO> entry : syncTaskTO.getTemplates().entrySet()) { + templateUtils.check(pullTaskTO.getTemplates(), ClientExceptionType.InvalidPullTask); + for (Map.Entry<String, AnyTO> entry : pullTaskTO.getTemplates().entrySet()) { AnyType type = anyTypeDAO.find(entry.getKey()); if (type == null) { LOG.debug("Invalid AnyType {} specified, ignoring...", entry.getKey()); } else { - AnyTemplateSyncTask anyTemplate = syncTask.getTemplate(type); + AnyTemplatePullTask anyTemplate = pullTask.getTemplate(type); if (anyTemplate == null) { - anyTemplate = entityFactory.newEntity(AnyTemplateSyncTask.class); + anyTemplate = entityFactory.newEntity(AnyTemplatePullTask.class); anyTemplate.setAnyType(type); - anyTemplate.setSyncTask(syncTask); + anyTemplate.setPullTask(pullTask); - syncTask.add(anyTemplate); + pullTask.add(anyTemplate); } anyTemplate.set(entry.getValue()); } } // remove all templates not contained in the TO - CollectionUtils.filter(syncTask.getTemplates(), new Predicate<AnyTemplate>() { + CollectionUtils.filter(pullTask.getTemplates(), new Predicate<AnyTemplate>() { @Override public boolean evaluate(final AnyTemplate anyTemplate) { - return syncTaskTO.getTemplates().containsKey(anyTemplate.getAnyType().getKey()); + return pullTaskTO.getTemplates().containsKey(anyTemplate.getAnyType().getKey()); } }); } @@ -190,7 +190,7 @@ public class TaskDataBinderImpl implements TaskDataBinder { task.setPerformCreate(taskTO.isPerformCreate()); task.setPerformUpdate(taskTO.isPerformUpdate()); task.setPerformDelete(taskTO.isPerformDelete()); - task.setSyncStatus(taskTO.isSyncStatus()); + task.setPullStatus(taskTO.isPullStatus()); task.getActionsClassNames().clear(); task.getActionsClassNames().addAll(taskTO.getActionsClassNames()); } @@ -334,21 +334,21 @@ public class TaskDataBinderImpl implements TaskDataBinder { setExecTime((SchedTaskTO) taskTO, task); break; - case SYNCHRONIZATION: - if (!(task instanceof SyncTask)) { - throw new IllegalArgumentException("taskUtils is type Sync but task is not SyncTask: " + case PULL: + if (!(task instanceof PullTask)) { + throw new IllegalArgumentException("taskUtils is type Pull but task is not PullTask: " + task.getClass().getName()); } setExecTime((SchedTaskTO) taskTO, task); - ((SyncTaskTO) taskTO).setDestinationRealm(((SyncTask) task).getDestinatioRealm().getFullPath()); - ((SyncTaskTO) taskTO).setResource(((SyncTask) task).getResource().getKey()); - ((SyncTaskTO) taskTO).setMatchingRule(((SyncTask) task).getMatchingRule() == null - ? MatchingRule.UPDATE : ((SyncTask) task).getMatchingRule()); - ((SyncTaskTO) taskTO).setUnmatchingRule(((SyncTask) task).getUnmatchingRule() == null - ? UnmatchingRule.PROVISION : ((SyncTask) task).getUnmatchingRule()); - - for (AnyTemplate template : ((SyncTask) task).getTemplates()) { - ((SyncTaskTO) taskTO).getTemplates().put(template.getAnyType().getKey(), template.get()); + ((PullTaskTO) taskTO).setDestinationRealm(((PullTask) task).getDestinatioRealm().getFullPath()); + ((PullTaskTO) taskTO).setResource(((PullTask) task).getResource().getKey()); + ((PullTaskTO) taskTO).setMatchingRule(((PullTask) task).getMatchingRule() == null + ? MatchingRule.UPDATE : ((PullTask) task).getMatchingRule()); + ((PullTaskTO) taskTO).setUnmatchingRule(((PullTask) task).getUnmatchingRule() == null + ? UnmatchingRule.PROVISION : ((PullTask) task).getUnmatchingRule()); + + for (AnyTemplate template : ((PullTask) task).getTemplates()) { + ((PullTaskTO) taskTO).getTemplates().put(template.getAnyType().getKey(), template.get()); } break; http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java index 6aa076e..4c6455b 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java @@ -46,7 +46,7 @@ import org.springframework.transaction.annotation.Transactional; * Simple action for propagating group memberships to LDAP groups, when the same resource is configured for both users * and groups. * - * @see org.apache.syncope.core.provisioning.java.sync.LDAPMembershipSyncActions + * @see org.apache.syncope.core.provisioning.java.pushpull.LDAPMembershipPullActions */ public class LDAPMembershipPropagationActions extends DefaultPropagationActions { http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractProvisioningJobDelegate.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractProvisioningJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractProvisioningJobDelegate.java new file mode 100644 index 0000000..d10d27f --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractProvisioningJobDelegate.java @@ -0,0 +1,434 @@ +/* + * 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.lang.reflect.ParameterizedType; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import javax.annotation.Resource; +import org.apache.syncope.common.lib.types.TraceLevel; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.dao.PolicyDAO; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.resource.Mapping; +import org.apache.syncope.core.persistence.api.entity.resource.Provision; +import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask; +import org.apache.syncope.core.persistence.api.entity.task.TaskExec; +import org.apache.syncope.core.provisioning.api.Connector; +import org.apache.syncope.core.provisioning.api.ConnectorFactory; +import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport; +import org.apache.syncope.core.provisioning.java.job.AbstractSchedTaskJobDelegate; +import org.apache.syncope.core.provisioning.java.job.TaskJob; +import org.quartz.JobExecutionException; +import org.springframework.beans.factory.annotation.Autowired; + +public abstract class AbstractProvisioningJobDelegate<T extends ProvisioningTask> + extends AbstractSchedTaskJobDelegate { + + @Resource(name = "adminUser") + protected String adminUser; + + /** + * ConnInstance loader. + */ + @Autowired + protected ConnectorFactory connFactory; + + @Autowired + protected AnyTypeDAO anyTypeDAO; + + /** + * Resource DAO. + */ + @Autowired + protected ExternalResourceDAO resourceDAO; + + /** + * Policy DAO. + */ + @Autowired + protected PolicyDAO policyDAO; + + /** + * Create a textual report of the provisionig operation, based on the trace level. + * + * @param provResults Provisioning results + * @param traceLevel Provisioning trace level + * @param dryRun dry run? + * @return report as string + */ + protected String createReport(final Collection<ProvisioningReport> provResults, final TraceLevel traceLevel, + final boolean dryRun) { + + if (traceLevel == TraceLevel.NONE) { + return null; + } + + StringBuilder report = new StringBuilder(); + + if (dryRun) { + report.append("==>Dry run only, no modifications were made<==\n\n"); + } + + List<ProvisioningReport> uSuccCreate = new ArrayList<>(); + List<ProvisioningReport> uFailCreate = new ArrayList<>(); + List<ProvisioningReport> uSuccUpdate = new ArrayList<>(); + List<ProvisioningReport> uFailUpdate = new ArrayList<>(); + List<ProvisioningReport> uSuccDelete = new ArrayList<>(); + List<ProvisioningReport> uFailDelete = new ArrayList<>(); + List<ProvisioningReport> uSuccNone = new ArrayList<>(); + List<ProvisioningReport> uIgnore = new ArrayList<>(); + List<ProvisioningReport> gSuccCreate = new ArrayList<>(); + List<ProvisioningReport> gFailCreate = new ArrayList<>(); + List<ProvisioningReport> gSuccUpdate = new ArrayList<>(); + List<ProvisioningReport> gFailUpdate = new ArrayList<>(); + List<ProvisioningReport> gSuccDelete = new ArrayList<>(); + List<ProvisioningReport> gFailDelete = new ArrayList<>(); + List<ProvisioningReport> gSuccNone = new ArrayList<>(); + List<ProvisioningReport> gIgnore = new ArrayList<>(); + List<ProvisioningReport> aSuccCreate = new ArrayList<>(); + List<ProvisioningReport> aFailCreate = new ArrayList<>(); + List<ProvisioningReport> aSuccUpdate = new ArrayList<>(); + List<ProvisioningReport> aFailUpdate = new ArrayList<>(); + List<ProvisioningReport> aSuccDelete = new ArrayList<>(); + List<ProvisioningReport> aFailDelete = new ArrayList<>(); + List<ProvisioningReport> aSuccNone = new ArrayList<>(); + List<ProvisioningReport> aIgnore = new ArrayList<>(); + + for (ProvisioningReport provResult : provResults) { + AnyType anyType = anyTypeDAO.find(provResult.getAnyType()); + + switch (provResult.getStatus()) { + case SUCCESS: + switch (provResult.getOperation()) { + case CREATE: + switch (anyType.getKind()) { + case USER: + uSuccCreate.add(provResult); + break; + + case GROUP: + gSuccCreate.add(provResult); + break; + + case ANY_OBJECT: + default: + aSuccCreate.add(provResult); + } + break; + + case UPDATE: + switch (anyType.getKind()) { + case USER: + uSuccUpdate.add(provResult); + break; + + case GROUP: + gSuccUpdate.add(provResult); + break; + + case ANY_OBJECT: + default: + aSuccUpdate.add(provResult); + } + break; + + case DELETE: + switch (anyType.getKind()) { + case USER: + uSuccDelete.add(provResult); + break; + + case GROUP: + gSuccDelete.add(provResult); + break; + + case ANY_OBJECT: + default: + aSuccDelete.add(provResult); + } + break; + + case NONE: + switch (anyType.getKind()) { + case USER: + uSuccNone.add(provResult); + break; + + case GROUP: + gSuccNone.add(provResult); + break; + + case ANY_OBJECT: + default: + aSuccNone.add(provResult); + } + break; + + default: + } + break; + + case FAILURE: + switch (provResult.getOperation()) { + case CREATE: + switch (anyType.getKind()) { + case USER: + uFailCreate.add(provResult); + break; + + case GROUP: + gFailCreate.add(provResult); + break; + + case ANY_OBJECT: + default: + aFailCreate.add(provResult); + } + break; + + case UPDATE: + switch (anyType.getKind()) { + case USER: + uFailUpdate.add(provResult); + break; + + case GROUP: + gFailUpdate.add(provResult); + break; + + case ANY_OBJECT: + default: + aFailUpdate.add(provResult); + } + break; + + case DELETE: + switch (anyType.getKind()) { + case USER: + uFailDelete.add(provResult); + break; + + case GROUP: + gFailDelete.add(provResult); + break; + + case ANY_OBJECT: + default: + aFailDelete.add(provResult); + } + break; + + default: + } + break; + + case IGNORE: + switch (anyType.getKind()) { + case USER: + uIgnore.add(provResult); + break; + + case GROUP: + gIgnore.add(provResult); + break; + + case ANY_OBJECT: + default: + aIgnore.add(provResult); + } + break; + + default: + } + } + + // Summary, also to be included for FAILURE and ALL, so create it anyway. + report.append("Users "). + append("[created/failures]: ").append(uSuccCreate.size()).append('/').append(uFailCreate.size()). + append(' '). + append("[updated/failures]: ").append(uSuccUpdate.size()).append('/').append(uFailUpdate.size()). + append(' '). + append("[deleted/failures]: ").append(uSuccDelete.size()).append('/').append(uFailDelete.size()). + append(' '). + append("[no operation/ignored]: ").append(uSuccNone.size()).append('/').append(uIgnore.size()). + append('\n'); + report.append("Groups "). + append("[created/failures]: ").append(gSuccCreate.size()).append('/').append(gFailCreate.size()). + append(' '). + append("[updated/failures]: ").append(gSuccUpdate.size()).append('/').append(gFailUpdate.size()). + append(' '). + append("[deleted/failures]: ").append(gSuccDelete.size()).append('/').append(gFailDelete.size()). + append(' '). + append("[no operation/ignored]: ").append(gSuccNone.size()).append('/').append(gIgnore.size()). + append('\n'); + report.append("Any objects "). + append("[created/failures]: ").append(aSuccCreate.size()).append('/').append(aFailCreate.size()). + append(' '). + append("[updated/failures]: ").append(aSuccUpdate.size()).append('/').append(aFailUpdate.size()). + append(' '). + append("[deleted/failures]: ").append(aSuccDelete.size()).append('/').append(aFailDelete.size()). + append(' '). + append("[no operation/ignored]: ").append(aSuccNone.size()).append('/').append(aIgnore.size()); + + // Failures + if (traceLevel == TraceLevel.FAILURES || traceLevel == TraceLevel.ALL) { + if (!uFailCreate.isEmpty()) { + report.append("\n\nUsers failed to create: "); + report.append(ProvisioningReport.produceReport(uFailCreate, traceLevel)); + } + if (!uFailUpdate.isEmpty()) { + report.append("\nUsers failed to update: "); + report.append(ProvisioningReport.produceReport(uFailUpdate, traceLevel)); + } + if (!uFailDelete.isEmpty()) { + report.append("\nUsers failed to delete: "); + report.append(ProvisioningReport.produceReport(uFailDelete, traceLevel)); + } + + if (!gFailCreate.isEmpty()) { + report.append("\n\nGroups failed to create: "); + report.append(ProvisioningReport.produceReport(gFailCreate, traceLevel)); + } + if (!gFailUpdate.isEmpty()) { + report.append("\nGroups failed to update: "); + report.append(ProvisioningReport.produceReport(gFailUpdate, traceLevel)); + } + if (!gFailDelete.isEmpty()) { + report.append("\nGroups failed to delete: "); + report.append(ProvisioningReport.produceReport(gFailDelete, traceLevel)); + } + + if (!aFailCreate.isEmpty()) { + report.append("\nAny objects failed to create: "); + report.append(ProvisioningReport.produceReport(aFailCreate, traceLevel)); + } + if (!aFailUpdate.isEmpty()) { + report.append("\nAny objects failed to update: "); + report.append(ProvisioningReport.produceReport(aFailUpdate, traceLevel)); + } + if (!aFailDelete.isEmpty()) { + report.append("\nAny objects failed to delete: "); + report.append(ProvisioningReport.produceReport(aFailDelete, traceLevel)); + } + } + + // Succeeded, only if on 'ALL' level + if (traceLevel == TraceLevel.ALL) { + report.append("\n\nUsers created:\n"). + append(ProvisioningReport.produceReport(uSuccCreate, traceLevel)). + append("\nUsers updated:\n"). + append(ProvisioningReport.produceReport(uSuccUpdate, traceLevel)). + append("\nUsers deleted:\n"). + append(ProvisioningReport.produceReport(uSuccDelete, traceLevel)). + append("\nUsers no operation:\n"). + append(ProvisioningReport.produceReport(uSuccNone, traceLevel)). + append("\nUsers ignored:\n"). + append(ProvisioningReport.produceReport(uIgnore, traceLevel)); + report.append("\n\nGroups created:\n"). + append(ProvisioningReport.produceReport(gSuccCreate, traceLevel)). + append("\nGroups updated:\n"). + append(ProvisioningReport.produceReport(gSuccUpdate, traceLevel)). + append("\nGroups deleted:\n"). + append(ProvisioningReport.produceReport(gSuccDelete, traceLevel)). + append("\nGroups no operation:\n"). + append(ProvisioningReport.produceReport(gSuccNone, traceLevel)). + append("\nGroups ignored:\n"). + append(ProvisioningReport.produceReport(gSuccNone, traceLevel)); + report.append("\n\nAny objects created:\n"). + append(ProvisioningReport.produceReport(aSuccCreate, traceLevel)). + append("\nAny objects updated:\n"). + append(ProvisioningReport.produceReport(aSuccUpdate, traceLevel)). + append("\nAny objects deleted:\n"). + append(ProvisioningReport.produceReport(aSuccDelete, traceLevel)). + append("\nAny objects no operation:\n"). + append(ProvisioningReport.produceReport(aSuccNone, traceLevel)). + append("\nAny objects ignored:\n"). + append(ProvisioningReport.produceReport(aSuccNone, traceLevel)); + } + + return report.toString(); + } + + @Override + protected String doExecute(final boolean dryRun) throws JobExecutionException { + try { + Class<T> clazz = getTaskClassReference(); + if (!clazz.isAssignableFrom(task.getClass())) { + throw new JobExecutionException("Task " + task.getKey() + " isn't a ProvisioningTask"); + } + + T provisioningTask = clazz.cast(task); + + Connector connector; + try { + connector = connFactory.getConnector(provisioningTask.getResource()); + } catch (Exception e) { + String msg = String.format("Connector instance bean for resource %s and connInstance %s not found", + provisioningTask.getResource(), provisioningTask.getResource().getConnector()); + throw new JobExecutionException(msg, e); + } + + boolean noMapping = true; + for (Provision provision : provisioningTask.getResource().getProvisions()) { + Mapping mapping = provision.getMapping(); + if (mapping != null) { + noMapping = false; + if (mapping.getConnObjectKeyItem() == null) { + throw new JobExecutionException( + "Invalid ConnObjectKey mapping for provision " + provision); + } + } + } + if (noMapping) { + return "No mapping configured for both users and groups: aborting..."; + } + + return doExecuteProvisioning( + provisioningTask, + connector, + dryRun); + } catch (Throwable t) { + LOG.error("While executing provisioning job {}", getClass().getName(), t); + throw t; + } + } + + protected abstract String doExecuteProvisioning( + final T task, + final Connector connector, + final boolean dryRun) throws JobExecutionException; + + @Override + protected boolean hasToBeRegistered(final TaskExec execution) { + final ProvisioningTask provTask = (ProvisioningTask) task; + + // True if either failed and failures have to be registered, or if ALL has to be registered. + return (TaskJob.Status.valueOf(execution.getStatus()) == TaskJob.Status.FAILURE + && provTask.getResource().getPullTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()) + || provTask.getResource().getPullTraceLevel().ordinal() >= TraceLevel.SUMMARY.ordinal(); + } + + @SuppressWarnings("unchecked") + private Class<T> getTaskClassReference() { + return (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]; + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java new file mode 100644 index 0000000..bde6ee7 --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java @@ -0,0 +1,797 @@ +/* + * 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.Collections; +import java.util.List; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.apache.syncope.common.lib.patch.AnyPatch; +import org.apache.syncope.common.lib.patch.StringPatchItem; +import org.apache.syncope.common.lib.to.AnyTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +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.PatchOperation; +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.dao.NotFoundException; +import org.apache.syncope.core.provisioning.api.propagation.PropagationException; +import org.apache.syncope.core.spring.security.DelegatedAdministrationException; +import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO; +import org.apache.syncope.core.persistence.api.entity.AnyUtils; +import org.apache.syncope.core.persistence.api.entity.VirSchema; +import org.apache.syncope.core.persistence.api.entity.resource.Provision; +import org.apache.syncope.core.provisioning.api.ProvisioningManager; +import org.apache.syncope.core.provisioning.api.cache.VirAttrCache; +import org.apache.syncope.core.provisioning.api.cache.VirAttrCacheValue; +import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException; +import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport; +import org.identityconnectors.framework.common.objects.Attribute; +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; +import org.springframework.transaction.annotation.Transactional; +import org.apache.syncope.core.persistence.api.entity.task.PullTask; +import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullResultHandler; +import org.apache.syncope.core.provisioning.api.pushpull.PullActions; + +@Transactional(rollbackFor = Throwable.class) +public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHandler<PullTask, PullActions> + implements SyncopePullResultHandler { + + @Autowired + protected PullUtils pullUtils; + + @Autowired + protected VirSchemaDAO virSchemaDAO; + + @Autowired + protected VirAttrCache virAttrCache; + + protected abstract String getName(AnyTO anyTO); + + protected abstract ProvisioningManager<?, ?> getProvisioningManager(); + + protected abstract AnyTO doCreate(AnyTO anyTO, SyncDelta delta, ProvisioningReport result); + + protected AnyTO doLink(final AnyTO before, final boolean unlink) { + AnyPatch patch = newPatch(before.getKey()); + patch.setKey(before.getKey()); + patch.getResources().add(new StringPatchItem.Builder(). + operation(unlink ? PatchOperation.DELETE : PatchOperation.ADD_REPLACE). + value(profile.getTask().getResource().getKey()).build()); + + return getAnyTO(update(patch).getResult()); + } + + protected abstract AnyTO doUpdate(AnyTO before, AnyPatch anyPatch, SyncDelta delta, ProvisioningReport result); + + protected void doDeprovision(final AnyTypeKind kind, final Long key, final boolean unlink) { + PropagationByResource propByRes = new PropagationByResource(); + propByRes.add(ResourceOperation.DELETE, profile.getTask().getResource().getKey()); + taskExecutor.execute(propagationManager.getDeleteTasks( + kind, + key, + propByRes, + null)); + + if (unlink) { + AnyPatch anyObjectPatch = newPatch(key); + anyObjectPatch.getResources().add(new StringPatchItem.Builder(). + operation(PatchOperation.DELETE). + value(profile.getTask().getResource().getKey()).build()); + } + } + + protected void doDelete(final AnyTypeKind kind, final Long key) { + PropagationByResource propByRes = new PropagationByResource(); + propByRes.add(ResourceOperation.DELETE, profile.getTask().getResource().getKey()); + try { + taskExecutor.execute(propagationManager.getDeleteTasks( + kind, + key, + propByRes, + null)); + } 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 anyObject " + key, e); + } + + getProvisioningManager().delete(key, true); + } + + @Override + public boolean handle(final SyncDelta delta) { + Provision provision = null; + try { + provision = profile.getTask().getResource().getProvision(delta.getObject().getObjectClass()); + if (provision == null) { + throw new JobExecutionException("No provision found on " + profile.getTask().getResource() + " for " + + delta.getObject().getObjectClass()); + } + + doHandle(delta, provision); + return true; + } catch (IgnoreProvisionException e) { + ProvisioningReport result = new ProvisioningReport(); + result.setOperation(ResourceOperation.NONE); + result.setAnyType(provision == null + ? getAnyUtils().getAnyTypeKind().name() : provision.getAnyType().getKey()); + result.setStatus(ProvisioningReport.Status.IGNORE); + result.setKey(0L); + result.setName(delta.getObject().getName().getNameValue()); + profile.getResults().add(result); + + LOG.warn("Ignoring during pull", e); + return true; + } catch (JobExecutionException e) { + LOG.error("Pull failed", e); + return false; + } + } + + protected List<ProvisioningReport> assign( + final SyncDelta delta, final Provision provision, final AnyUtils anyUtils) + throws JobExecutionException { + + if (!profile.getTask().isPerformCreate()) { + LOG.debug("PullTask not configured for create"); + return Collections.<ProvisioningReport>emptyList(); + } + + AnyTO anyTO = connObjectUtils.getAnyTO(delta.getObject(), profile.getTask(), provision, anyUtils); + + anyTO.getResources().add(profile.getTask().getResource().getKey()); + + ProvisioningReport result = new ProvisioningReport(); + result.setOperation(ResourceOperation.CREATE); + result.setAnyType(provision.getAnyType().getKey()); + result.setStatus(ProvisioningReport.Status.SUCCESS); + result.setName(getName(anyTO)); + + if (profile.isDryRun()) { + result.setKey(0L); + } else { + SyncDelta actionedDelta = delta; + for (PullActions action : profile.getActions()) { + actionedDelta = action.beforeAssign(this.getProfile(), actionedDelta, anyTO); + } + + create(anyTO, actionedDelta, UnmatchingRule.toEventName(UnmatchingRule.ASSIGN), result); + } + + return Collections.singletonList(result); + } + + protected List<ProvisioningReport> provision( + final SyncDelta delta, final Provision provision, final AnyUtils anyUtils) + throws JobExecutionException { + + if (!profile.getTask().isPerformCreate()) { + LOG.debug("PullTask not configured for create"); + return Collections.<ProvisioningReport>emptyList(); + } + + AnyTO anyTO = connObjectUtils.getAnyTO(delta.getObject(), profile.getTask(), provision, anyUtils); + + ProvisioningReport result = new ProvisioningReport(); + result.setOperation(ResourceOperation.CREATE); + result.setAnyType(provision.getAnyType().getKey()); + result.setStatus(ProvisioningReport.Status.SUCCESS); + result.setName(getName(anyTO)); + + if (profile.isDryRun()) { + result.setKey(0L); + } else { + SyncDelta actionedDelta = delta; + for (PullActions action : profile.getActions()) { + actionedDelta = action.beforeProvision(this.getProfile(), actionedDelta, anyTO); + } + + create(anyTO, actionedDelta, UnmatchingRule.toEventName(UnmatchingRule.PROVISION), result); + } + + return Collections.singletonList(result); + } + + private void create( + final AnyTO anyTO, + final SyncDelta delta, + final String operation, + final ProvisioningReport result) + throws JobExecutionException { + + Object output; + Result resultStatus; + + try { + AnyTO actual = doCreate(anyTO, delta, result); + result.setName(getName(actual)); + output = actual; + resultStatus = Result.SUCCESS; + + for (PullActions action : profile.getActions()) { + action.after(this.getProfile(), delta, actual, result); + } + } catch (IgnoreProvisionException e) { + throw e; + } catch (PropagationException 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 {} {}", anyTO.getType(), delta.getUid().getUidValue(), e); + output = e; + resultStatus = Result.FAILURE; + + for (PullActions action : profile.getActions()) { + action.onError(this.getProfile(), delta, result, e); + } + } catch (Exception e) { + result.setStatus(ProvisioningReport.Status.FAILURE); + result.setMessage(ExceptionUtils.getRootCauseMessage(e)); + LOG.error("Could not create {} {} ", anyTO.getType(), delta.getUid().getUidValue(), e); + output = e; + resultStatus = Result.FAILURE; + + for (PullActions action : profile.getActions()) { + action.onError(this.getProfile(), delta, result, e); + } + } + + audit(operation, resultStatus, null, output, delta); + } + + protected List<ProvisioningReport> update(final SyncDelta delta, final List<Long> anys, + final Provision provision) throws JobExecutionException { + + if (!profile.getTask().isPerformUpdate()) { + LOG.debug("PullTask not configured for update"); + return Collections.<ProvisioningReport>emptyList(); + } + + LOG.debug("About to update {}", anys); + + List<ProvisioningReport> results = new ArrayList<>(); + + SyncDelta workingDelta = delta; + for (Long key : anys) { + LOG.debug("About to update {}", key); + + ProvisioningReport result = new ProvisioningReport(); + result.setOperation(ResourceOperation.UPDATE); + result.setAnyType(provision.getAnyType().getKey()); + result.setStatus(ProvisioningReport.Status.SUCCESS); + result.setKey(key); + + AnyTO before = getAnyTO(key); + if (before == null) { + result.setStatus(ProvisioningReport.Status.FAILURE); + result.setMessage(String.format("Any '%s(%d)' not found", provision.getAnyType().getKey(), key)); + } else { + result.setName(getName(before)); + } + + Result resultStatus; + Object output; + if (!profile.isDryRun()) { + if (before == null) { + resultStatus = Result.FAILURE; + output = null; + } else { + try { + AnyPatch anyPatch = connObjectUtils.getAnyPatch( + before.getKey(), + workingDelta.getObject(), + before, + profile.getTask(), + provision, + getAnyUtils()); + + for (PullActions action : profile.getActions()) { + workingDelta = action.beforeUpdate(this.getProfile(), workingDelta, before, anyPatch); + } + + AnyTO updated = doUpdate(before, anyPatch, workingDelta, result); + + for (PullActions action : profile.getActions()) { + action.after(this.getProfile(), workingDelta, updated, result); + } + + output = updated; + resultStatus = Result.SUCCESS; + result.setName(getName(updated)); + LOG.debug("{} {} successfully updated", provision.getAnyType().getKey(), key); + } catch (IgnoreProvisionException e) { + throw e; + } catch (PropagationException 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 {} {}", + provision.getAnyType().getKey(), workingDelta.getUid().getUidValue(), e); + output = e; + resultStatus = Result.FAILURE; + + for (PullActions action : profile.getActions()) { + action.onError(this.getProfile(), workingDelta, result, e); + } + } catch (Exception e) { + result.setStatus(ProvisioningReport.Status.FAILURE); + result.setMessage(ExceptionUtils.getRootCauseMessage(e)); + LOG.error("Could not update {} {}", + provision.getAnyType().getKey(), workingDelta.getUid().getUidValue(), e); + output = e; + resultStatus = Result.FAILURE; + + for (PullActions action : profile.getActions()) { + action.onError(this.getProfile(), workingDelta, result, e); + } + } + } + audit(MatchingRule.toEventName(MatchingRule.UPDATE), resultStatus, before, output, workingDelta); + } + results.add(result); + } + return results; + } + + protected List<ProvisioningReport> deprovision( + final SyncDelta delta, + final List<Long> anys, + final Provision provision, + final boolean unlink) + throws JobExecutionException { + + if (!profile.getTask().isPerformUpdate()) { + LOG.debug("PullTask not configured for update"); + return Collections.<ProvisioningReport>emptyList(); + } + + LOG.debug("About to update {}", anys); + + final List<ProvisioningReport> updResults = new ArrayList<>(); + + for (Long key : anys) { + LOG.debug("About to unassign resource {}", key); + + Object output; + Result resultStatus; + + ProvisioningReport result = new ProvisioningReport(); + result.setOperation(ResourceOperation.DELETE); + result.setAnyType(provision.getAnyType().getKey()); + result.setStatus(ProvisioningReport.Status.SUCCESS); + result.setKey(key); + + AnyTO before = getAnyTO(key); + + if (before == null) { + result.setStatus(ProvisioningReport.Status.FAILURE); + result.setMessage(String.format("Any '%s(%d)' not found", provision.getAnyType().getKey(), key)); + } + + if (!profile.isDryRun()) { + if (before == null) { + resultStatus = Result.FAILURE; + output = null; + } else { + result.setName(getName(before)); + + try { + if (unlink) { + for (PullActions action : profile.getActions()) { + action.beforeUnassign(this.getProfile(), delta, before); + } + } else { + for (PullActions action : profile.getActions()) { + action.beforeDeprovision(this.getProfile(), delta, before); + } + } + + doDeprovision(provision.getAnyType().getKind(), key, unlink); + output = getAnyTO(key); + + for (PullActions action : profile.getActions()) { + action.after(this.getProfile(), delta, AnyTO.class.cast(output), result); + } + + resultStatus = Result.SUCCESS; + LOG.debug("{} {} successfully updated", provision.getAnyType().getKey(), key); + } catch (IgnoreProvisionException e) { + throw e; + } catch (PropagationException 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 {} {}", + provision.getAnyType().getKey(), delta.getUid().getUidValue(), e); + output = e; + resultStatus = Result.FAILURE; + + for (PullActions action : profile.getActions()) { + action.onError(this.getProfile(), delta, result, e); + } + } catch (Exception e) { + result.setStatus(ProvisioningReport.Status.FAILURE); + result.setMessage(ExceptionUtils.getRootCauseMessage(e)); + LOG.error("Could not update {} {}", + provision.getAnyType().getKey(), delta.getUid().getUidValue(), e); + output = e; + resultStatus = Result.FAILURE; + + for (PullActions action : profile.getActions()) { + action.onError(this.getProfile(), delta, result, e); + } + } + } + audit(unlink + ? MatchingRule.toEventName(MatchingRule.UNASSIGN) + : MatchingRule.toEventName(MatchingRule.DEPROVISION), resultStatus, before, output, delta); + } + updResults.add(result); + } + + return updResults; + } + + protected List<ProvisioningReport> link( + final SyncDelta delta, + final List<Long> anys, + final Provision provision, + final boolean unlink) + throws JobExecutionException { + + if (!profile.getTask().isPerformUpdate()) { + LOG.debug("PullTask not configured for update"); + return Collections.<ProvisioningReport>emptyList(); + } + + LOG.debug("About to update {}", anys); + + final List<ProvisioningReport> updResults = new ArrayList<>(); + + for (Long key : anys) { + LOG.debug("About to unassign resource {}", key); + + Object output; + Result resultStatus; + + ProvisioningReport result = new ProvisioningReport(); + result.setOperation(ResourceOperation.NONE); + result.setAnyType(provision.getAnyType().getKey()); + result.setStatus(ProvisioningReport.Status.SUCCESS); + result.setKey(key); + + AnyTO before = getAnyTO(key); + + if (before == null) { + result.setStatus(ProvisioningReport.Status.FAILURE); + result.setMessage(String.format("Any '%s(%d)' not found", provision.getAnyType().getKey(), key)); + } + + if (!profile.isDryRun()) { + if (before == null) { + resultStatus = Result.FAILURE; + output = null; + } else { + result.setName(getName(before)); + + try { + if (unlink) { + for (PullActions action : profile.getActions()) { + action.beforeUnlink(this.getProfile(), delta, before); + } + } else { + for (PullActions action : profile.getActions()) { + action.beforeLink(this.getProfile(), delta, before); + } + } + + output = doLink(before, unlink); + + for (PullActions action : profile.getActions()) { + action.after(this.getProfile(), delta, AnyTO.class.cast(output), result); + } + + resultStatus = Result.SUCCESS; + LOG.debug("{} {} successfully updated", provision.getAnyType().getKey(), key); + } catch (IgnoreProvisionException e) { + throw e; + } catch (PropagationException 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 {} {}", + provision.getAnyType().getKey(), delta.getUid().getUidValue(), e); + output = e; + resultStatus = Result.FAILURE; + + for (PullActions action : profile.getActions()) { + action.onError(this.getProfile(), delta, result, e); + } + } catch (Exception e) { + result.setStatus(ProvisioningReport.Status.FAILURE); + result.setMessage(ExceptionUtils.getRootCauseMessage(e)); + LOG.error("Could not update {} {}", + provision.getAnyType().getKey(), delta.getUid().getUidValue(), e); + output = e; + resultStatus = Result.FAILURE; + + for (PullActions action : profile.getActions()) { + action.onError(this.getProfile(), delta, result, e); + } + } + } + audit(unlink ? MatchingRule.toEventName(MatchingRule.UNLINK) + : MatchingRule.toEventName(MatchingRule.LINK), resultStatus, before, output, delta); + } + updResults.add(result); + } + + return updResults; + } + + protected List<ProvisioningReport> delete( + final SyncDelta delta, + final List<Long> anys, + final Provision provision) + throws JobExecutionException { + + if (!profile.getTask().isPerformDelete()) { + LOG.debug("PullTask not configured for delete"); + return Collections.<ProvisioningReport>emptyList(); + } + + LOG.debug("About to delete {}", anys); + + List<ProvisioningReport> delResults = new ArrayList<>(); + + SyncDelta workingDelta = delta; + for (Long key : anys) { + Object output; + Result resultStatus = Result.FAILURE; + + ProvisioningReport result = new ProvisioningReport(); + + try { + AnyTO before = getAnyTO(key); + + result.setKey(key); + result.setName(getName(before)); + result.setOperation(ResourceOperation.DELETE); + result.setAnyType(provision.getAnyType().getKey()); + result.setStatus(ProvisioningReport.Status.SUCCESS); + + if (!profile.isDryRun()) { + for (PullActions action : profile.getActions()) { + workingDelta = action.beforeDelete(this.getProfile(), workingDelta, before); + } + + try { + doDelete(provision.getAnyType().getKind(), key); + output = null; + resultStatus = Result.SUCCESS; + + for (PullActions action : profile.getActions()) { + action.after(this.getProfile(), workingDelta, before, result); + } + } catch (IgnoreProvisionException e) { + throw e; + } catch (Exception e) { + result.setStatus(ProvisioningReport.Status.FAILURE); + result.setMessage(ExceptionUtils.getRootCauseMessage(e)); + LOG.error("Could not delete {} {}", provision.getAnyType().getKey(), key, e); + output = e; + + for (PullActions action : profile.getActions()) { + action.onError(this.getProfile(), workingDelta, result, e); + } + } + + audit(ResourceOperation.DELETE.name().toLowerCase(), resultStatus, before, output, workingDelta); + } + + delResults.add(result); + } catch (NotFoundException e) { + LOG.error("Could not find {} {}", provision.getAnyType().getKey(), key, e); + } catch (DelegatedAdministrationException e) { + LOG.error("Not allowed to read {} {}", provision.getAnyType().getKey(), key, e); + } catch (Exception e) { + LOG.error("Could not delete {} {}", provision.getAnyType().getKey(), key, e); + } + } + + return delResults; + } + + private List<ProvisioningReport> ignore( + final SyncDelta delta, + final Provision provision, + final boolean matching) + throws JobExecutionException { + + LOG.debug("Any to ignore {}", delta.getObject().getUid().getUidValue()); + + final List<ProvisioningReport> ignoreResults = new ArrayList<>(); + ProvisioningReport result = new ProvisioningReport(); + + result.setKey(null); + result.setName(delta.getObject().getUid().getUidValue()); + result.setOperation(ResourceOperation.NONE); + result.setAnyType(provision.getAnyType().getKey()); + result.setStatus(ProvisioningReport.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 any object(s). + * + * @param delta returned by the underlying profile.getConnector() + * @param provision provisioning info + * @throws JobExecutionException in case of pull failure. + */ + protected void doHandle(final SyncDelta delta, final Provision provision) throws JobExecutionException { + AnyUtils anyUtils = getAnyUtils(); + + LOG.debug("Process {} for {} as {}", + delta.getDeltaType(), delta.getUid().getUidValue(), delta.getObject().getObjectClass()); + + String uid = delta.getPreviousUid() == null + ? delta.getUid().getUidValue() + : delta.getPreviousUid().getUidValue(); + + try { + List<Long> anyKeys = pullUtils.findExisting(uid, delta.getObject(), provision, anyUtils); + LOG.debug("Match(es) found for {} as {}: {}", + delta.getUid().getUidValue(), delta.getObject().getObjectClass(), anyKeys); + + if (anyKeys.size() > 1) { + switch (profile.getResAct()) { + case IGNORE: + throw new IllegalStateException("More than one match " + anyKeys); + + case FIRSTMATCH: + anyKeys = anyKeys.subList(0, 1); + break; + + case LASTMATCH: + anyKeys = anyKeys.subList(anyKeys.size() - 1, anyKeys.size()); + break; + + default: + // keep anyKeys unmodified + } + } + + if (SyncDeltaType.CREATE_OR_UPDATE == delta.getDeltaType()) { + if (anyKeys.isEmpty()) { + switch (profile.getTask().getUnmatchingRule()) { + case ASSIGN: + profile.getResults().addAll(assign(delta, provision, anyUtils)); + break; + + case PROVISION: + profile.getResults().addAll(provision(delta, provision, anyUtils)); + break; + + case IGNORE: + profile.getResults().addAll(ignore(delta, provision, false)); + break; + + default: + // do nothing + } + } else { + // update VirAttrCache + for (VirSchema virSchema : virSchemaDAO.findByProvision(provision)) { + Attribute attr = delta.getObject().getAttributeByName(virSchema.getExtAttrName()); + for (Long anyKey : anyKeys) { + if (attr == null) { + virAttrCache.expire( + provision.getAnyType().getKey(), + anyKey, + virSchema.getKey()); + } else { + VirAttrCacheValue cacheValue = new VirAttrCacheValue(); + cacheValue.setValues(attr.getValue()); + virAttrCache.put( + provision.getAnyType().getKey(), + anyKey, + virSchema.getKey(), + cacheValue); + } + } + } + + switch (profile.getTask().getMatchingRule()) { + case UPDATE: + profile.getResults().addAll(update(delta, anyKeys, provision)); + break; + + case DEPROVISION: + profile.getResults().addAll(deprovision(delta, anyKeys, provision, false)); + break; + + case UNASSIGN: + profile.getResults().addAll(deprovision(delta, anyKeys, provision, true)); + break; + + case LINK: + profile.getResults().addAll(link(delta, anyKeys, provision, false)); + break; + + case UNLINK: + profile.getResults().addAll(link(delta, anyKeys, provision, true)); + break; + + case IGNORE: + profile.getResults().addAll(ignore(delta, provision, true)); + break; + + default: + // do nothing + } + } + } else if (SyncDeltaType.DELETE == delta.getDeltaType()) { + if (anyKeys.isEmpty()) { + LOG.debug("No match found for deletion"); + } else { + profile.getResults().addAll(delete(delta, anyKeys, provision)); + } + } + } 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.PULL, + getAnyUtils().getAnyTypeKind().name().toLowerCase(), + profile.getTask().getResource().getKey(), + event, + result, + before, + output, + input); + + auditManager.audit(AuditElements.EventCategoryType.PULL, + getAnyUtils().getAnyTypeKind().name().toLowerCase(), + profile.getTask().getResource().getKey(), + event, + result, + before, + output, + input); + } +}
