You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2016/03/09 12:52:42 UTC
[06/17] syncope git commit: Further refactoring as per SYNCOPE-620
http://git-wip-us.apache.org/repos/asf/syncope/blob/28569df5/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AbstractProvisioningJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AbstractProvisioningJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AbstractProvisioningJobDelegate.java
new file mode 100644
index 0000000..bd13365
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/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.syncpull;
+
+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.syncpull.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 synchronization, based on the trace level.
+ *
+ * @param provResults Sync results
+ * @param syncTraceLevel Sync trace level
+ * @param dryRun dry run?
+ * @return report as string
+ */
+ protected String createReport(final Collection<ProvisioningReport> provResults, final TraceLevel syncTraceLevel,
+ final boolean dryRun) {
+
+ if (syncTraceLevel == 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 (syncTraceLevel == TraceLevel.FAILURES || syncTraceLevel == TraceLevel.ALL) {
+ if (!uFailCreate.isEmpty()) {
+ report.append("\n\nUsers failed to create: ");
+ report.append(ProvisioningReport.produceReport(uFailCreate, syncTraceLevel));
+ }
+ if (!uFailUpdate.isEmpty()) {
+ report.append("\nUsers failed to update: ");
+ report.append(ProvisioningReport.produceReport(uFailUpdate, syncTraceLevel));
+ }
+ if (!uFailDelete.isEmpty()) {
+ report.append("\nUsers failed to delete: ");
+ report.append(ProvisioningReport.produceReport(uFailDelete, syncTraceLevel));
+ }
+
+ if (!gFailCreate.isEmpty()) {
+ report.append("\n\nGroups failed to create: ");
+ report.append(ProvisioningReport.produceReport(gFailCreate, syncTraceLevel));
+ }
+ if (!gFailUpdate.isEmpty()) {
+ report.append("\nGroups failed to update: ");
+ report.append(ProvisioningReport.produceReport(gFailUpdate, syncTraceLevel));
+ }
+ if (!gFailDelete.isEmpty()) {
+ report.append("\nGroups failed to delete: ");
+ report.append(ProvisioningReport.produceReport(gFailDelete, syncTraceLevel));
+ }
+
+ if (!aFailCreate.isEmpty()) {
+ report.append("\nAny objects failed to create: ");
+ report.append(ProvisioningReport.produceReport(aFailCreate, syncTraceLevel));
+ }
+ if (!aFailUpdate.isEmpty()) {
+ report.append("\nAny objects failed to update: ");
+ report.append(ProvisioningReport.produceReport(aFailUpdate, syncTraceLevel));
+ }
+ if (!aFailDelete.isEmpty()) {
+ report.append("\nAny objects failed to delete: ");
+ report.append(ProvisioningReport.produceReport(aFailDelete, syncTraceLevel));
+ }
+ }
+
+ // Succeeded, only if on 'ALL' level
+ if (syncTraceLevel == TraceLevel.ALL) {
+ report.append("\n\nUsers created:\n").
+ append(ProvisioningReport.produceReport(uSuccCreate, syncTraceLevel)).
+ append("\nUsers updated:\n").
+ append(ProvisioningReport.produceReport(uSuccUpdate, syncTraceLevel)).
+ append("\nUsers deleted:\n").
+ append(ProvisioningReport.produceReport(uSuccDelete, syncTraceLevel)).
+ append("\nUsers no operation:\n").
+ append(ProvisioningReport.produceReport(uSuccNone, syncTraceLevel)).
+ append("\nUsers ignored:\n").
+ append(ProvisioningReport.produceReport(uIgnore, syncTraceLevel));
+ report.append("\n\nGroups created:\n").
+ append(ProvisioningReport.produceReport(gSuccCreate, syncTraceLevel)).
+ append("\nGroups updated:\n").
+ append(ProvisioningReport.produceReport(gSuccUpdate, syncTraceLevel)).
+ append("\nGroups deleted:\n").
+ append(ProvisioningReport.produceReport(gSuccDelete, syncTraceLevel)).
+ append("\nGroups no operation:\n").
+ append(ProvisioningReport.produceReport(gSuccNone, syncTraceLevel)).
+ append("\nGroups ignored:\n").
+ append(ProvisioningReport.produceReport(gSuccNone, syncTraceLevel));
+ report.append("\n\nAny objects created:\n").
+ append(ProvisioningReport.produceReport(aSuccCreate, syncTraceLevel)).
+ append("\nAny objects updated:\n").
+ append(ProvisioningReport.produceReport(aSuccUpdate, syncTraceLevel)).
+ append("\nAny objects deleted:\n").
+ append(ProvisioningReport.produceReport(aSuccDelete, syncTraceLevel)).
+ append("\nAny objects no operation:\n").
+ append(ProvisioningReport.produceReport(aSuccNone, syncTraceLevel)).
+ append("\nAny objects ignored:\n").
+ append(ProvisioningReport.produceReport(aSuccNone, syncTraceLevel));
+ }
+
+ 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().getSyncTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal())
+ || provTask.getResource().getSyncTraceLevel().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/28569df5/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AbstractPushResultHandler.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AbstractPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AbstractPushResultHandler.java
new file mode 100644
index 0000000..258e106
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AbstractPushResultHandler.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.syncpull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.apache.commons.collections4.IteratorUtils;
+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.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.entity.task.PushTask;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.syncpull.ProvisioningReport;
+import org.apache.syncope.core.provisioning.api.syncpull.PushActions;
+import org.apache.syncope.core.provisioning.java.MappingManagerImpl;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
+import org.apache.syncope.core.persistence.api.entity.group.Group;
+import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
+import org.apache.syncope.core.persistence.api.entity.resource.Provision;
+import org.apache.syncope.core.provisioning.api.MappingManager;
+import org.apache.syncope.core.provisioning.api.TimeoutException;
+import org.apache.syncope.core.provisioning.api.syncpull.IgnoreProvisionException;
+import org.apache.syncope.core.provisioning.api.syncpull.SyncopePushResultHandler;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.identityconnectors.framework.common.objects.Uid;
+import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHandler<PushTask, PushActions>
+ implements SyncopePushResultHandler {
+
+ @Autowired
+ protected MappingManager mappingManager;
+
+ protected abstract String getName(Any<?> any);
+
+ protected void deprovision(final Any<?> any) {
+ AnyTO before = getAnyTO(any.getKey());
+
+ List<String> noPropResources = new ArrayList<>(before.getResources());
+ noPropResources.remove(profile.getTask().getResource().getKey());
+
+ taskExecutor.execute(propagationManager.getDeleteTasks(
+ any.getType().getKind(),
+ any.getKey(),
+ null,
+ noPropResources));
+ }
+
+ protected void provision(final Any<?> any, final Boolean enabled) {
+ AnyTO before = getAnyTO(any.getKey());
+
+ List<String> noPropResources = new ArrayList<>(before.getResources());
+ noPropResources.remove(profile.getTask().getResource().getKey());
+
+ PropagationByResource propByRes = new PropagationByResource();
+ propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
+
+ taskExecutor.execute(propagationManager.getCreateTasks(
+ any.getType().getKind(),
+ any.getKey(),
+ propByRes,
+ before.getVirAttrs(),
+ noPropResources));
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void link(final Any<?> any, final Boolean unlink) {
+ AnyPatch patch = newPatch(any.getKey());
+ patch.getResources().add(new StringPatchItem.Builder().
+ operation(unlink ? PatchOperation.DELETE : PatchOperation.ADD_REPLACE).
+ value(profile.getTask().getResource().getKey()).build());
+
+ update(patch);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void unassign(final Any<?> any) {
+ AnyPatch patch = newPatch(any.getKey());
+ patch.getResources().add(new StringPatchItem.Builder().
+ operation(PatchOperation.DELETE).
+ value(profile.getTask().getResource().getKey()).build());
+
+ update(patch);
+
+ deprovision(any);
+ }
+
+ protected void assign(final Any<?> any, final Boolean enabled) {
+ AnyPatch patch = newPatch(any.getKey());
+ patch.getResources().add(new StringPatchItem.Builder().
+ operation(PatchOperation.ADD_REPLACE).
+ value(profile.getTask().getResource().getKey()).build());
+
+ update(patch);
+
+ provision(any, enabled);
+ }
+
+ protected ConnectorObject getRemoteObject(final String connObjectKey, final ObjectClass objectClass) {
+ ConnectorObject obj = null;
+ try {
+ Uid uid = new Uid(connObjectKey);
+
+ obj = profile.getConnector().getObject(objectClass,
+ uid,
+ MappingManagerImpl.buildOperationOptions(IteratorUtils.<MappingItem>emptyIterator()));
+ } catch (TimeoutException toe) {
+ LOG.debug("Request timeout", toe);
+ throw toe;
+ } catch (RuntimeException ignore) {
+ LOG.debug("While resolving {}", connObjectKey, ignore);
+ }
+
+ return obj;
+ }
+
+ @Transactional(propagation = Propagation.REQUIRES_NEW)
+ @Override
+ public boolean handle(final long anyKey) {
+ Any<?> any = null;
+ try {
+ any = getAny(anyKey);
+ doHandle(any);
+ return true;
+ } catch (IgnoreProvisionException e) {
+ ProvisioningReport result = new ProvisioningReport();
+ result.setOperation(ResourceOperation.NONE);
+ result.setAnyType(any == null ? null : any.getType().getKey());
+ result.setStatus(ProvisioningReport.Status.IGNORE);
+ result.setKey(anyKey);
+ 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 Any<?> any) throws JobExecutionException {
+ AnyUtils anyUtils = anyUtilsFactory.getInstance(any);
+
+ ProvisioningReport result = new ProvisioningReport();
+ profile.getResults().add(result);
+
+ result.setKey(any.getKey());
+ result.setAnyType(any.getType().getKey());
+ result.setName(getName(any));
+
+ Boolean enabled = any instanceof User && profile.getTask().isSyncStatus()
+ ? ((User) any).isSuspended() ? Boolean.FALSE : Boolean.TRUE
+ : null;
+
+ LOG.debug("Propagating {} with key {} towards {}",
+ anyUtils.getAnyTypeKind(), any.getKey(), profile.getTask().getResource());
+
+ Object output = null;
+ Result resultStatus = null;
+ String operation = null;
+
+ // Try to read remote object BEFORE any actual operation
+ Provision provision = profile.getTask().getResource().getProvision(any.getType());
+ String connObjecKey = mappingManager.getConnObjectKeyValue(any, provision);
+
+ ConnectorObject beforeObj = getRemoteObject(connObjecKey, provision.getObjectClass());
+
+ Boolean status = profile.getTask().isSyncStatus() ? enabled : null;
+
+ if (profile.isDryRun()) {
+ if (beforeObj == null) {
+ result.setOperation(getResourceOperation(profile.getTask().getUnmatchingRule()));
+ } else {
+ result.setOperation(getResourceOperation(profile.getTask().getMatchingRule()));
+ }
+ result.setStatus(ProvisioningReport.Status.SUCCESS);
+ } else {
+ try {
+ if (beforeObj == null) {
+ operation = UnmatchingRule.toEventName(profile.getTask().getUnmatchingRule());
+ result.setOperation(getResourceOperation(profile.getTask().getUnmatchingRule()));
+
+ switch (profile.getTask().getUnmatchingRule()) {
+ case ASSIGN:
+ for (PushActions action : profile.getActions()) {
+ action.beforeAssign(this.getProfile(), any);
+ }
+
+ if (!profile.getTask().isPerformCreate()) {
+ LOG.debug("PushTask not configured for create");
+ } else {
+ assign(any, status);
+ }
+
+ break;
+
+ case PROVISION:
+ for (PushActions action : profile.getActions()) {
+ action.beforeProvision(this.getProfile(), any);
+ }
+
+ if (!profile.getTask().isPerformCreate()) {
+ LOG.debug("PushTask not configured for create");
+ } else {
+ provision(any, status);
+ }
+
+ break;
+
+ case UNLINK:
+ for (PushActions action : profile.getActions()) {
+ action.beforeUnlink(this.getProfile(), any);
+ }
+
+ if (!profile.getTask().isPerformUpdate()) {
+ LOG.debug("PushTask not configured for update");
+ } else {
+ link(any, true);
+ }
+
+ break;
+
+ case IGNORE:
+ LOG.debug("Ignored any: {}", any);
+ break;
+ default:
+ // do nothing
+ }
+ } else {
+ operation = MatchingRule.toEventName(profile.getTask().getMatchingRule());
+ result.setOperation(getResourceOperation(profile.getTask().getMatchingRule()));
+
+ switch (profile.getTask().getMatchingRule()) {
+ case UPDATE:
+ for (PushActions action : profile.getActions()) {
+ action.beforeUpdate(this.getProfile(), any);
+ }
+ if (!profile.getTask().isPerformUpdate()) {
+ LOG.debug("PushTask not configured for update");
+ } else {
+ update(any, status);
+ }
+
+ break;
+
+ case DEPROVISION:
+ for (PushActions action : profile.getActions()) {
+ action.beforeDeprovision(this.getProfile(), any);
+ }
+
+ if (!profile.getTask().isPerformDelete()) {
+ LOG.debug("PushTask not configured for delete");
+ } else {
+ deprovision(any);
+ }
+
+ break;
+
+ case UNASSIGN:
+ for (PushActions action : profile.getActions()) {
+ action.beforeUnassign(this.getProfile(), any);
+ }
+
+ if (!profile.getTask().isPerformDelete()) {
+ LOG.debug("PushTask not configured for delete");
+ } else {
+ unassign(any);
+ }
+
+ break;
+
+ case LINK:
+ for (PushActions action : profile.getActions()) {
+ action.beforeLink(this.getProfile(), any);
+ }
+
+ if (!profile.getTask().isPerformUpdate()) {
+ LOG.debug("PushTask not configured for update");
+ } else {
+ link(any, false);
+ }
+
+ break;
+
+ case UNLINK:
+ for (PushActions action : profile.getActions()) {
+ action.beforeUnlink(this.getProfile(), any);
+ }
+
+ if (!profile.getTask().isPerformUpdate()) {
+ LOG.debug("PushTask not configured for update");
+ } else {
+ link(any, true);
+ }
+
+ break;
+
+ case IGNORE:
+ LOG.debug("Ignored any: {}", any);
+ break;
+ default:
+ // do nothing
+ }
+ }
+
+ for (PushActions action : profile.getActions()) {
+ action.after(this.getProfile(), any, result);
+ }
+
+ result.setStatus(ProvisioningReport.Status.SUCCESS);
+ resultStatus = AuditElements.Result.SUCCESS;
+ output = getRemoteObject(connObjecKey, provision.getObjectClass());
+ } catch (IgnoreProvisionException e) {
+ throw e;
+ } catch (Exception e) {
+ result.setStatus(ProvisioningReport.Status.FAILURE);
+ result.setMessage(ExceptionUtils.getRootCauseMessage(e));
+ resultStatus = AuditElements.Result.FAILURE;
+ output = e;
+
+ LOG.warn("Error pushing {} towards {}", any, profile.getTask().getResource(), e);
+
+ for (PushActions action : profile.getActions()) {
+ action.onError(this.getProfile(), any, result, e);
+ }
+
+ throw new JobExecutionException(e);
+ } finally {
+ notificationManager.createTasks(AuditElements.EventCategoryType.PUSH,
+ any.getType().getKind().name().toLowerCase(),
+ profile.getTask().getResource().getKey(),
+ operation,
+ resultStatus,
+ beforeObj,
+ output,
+ any);
+ auditManager.audit(AuditElements.EventCategoryType.PUSH,
+ any.getType().getKind().name().toLowerCase(),
+ profile.getTask().getResource().getKey(),
+ operation,
+ resultStatus,
+ connObjectUtils.getConnObjectTO(beforeObj),
+ output instanceof ConnectorObject
+ ? connObjectUtils.getConnObjectTO((ConnectorObject) output) : output,
+ any);
+ }
+ }
+ }
+
+ 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 Any<?> update(final Any<?> any, final Boolean enabled) {
+ boolean changepwd;
+ Collection<String> resourceNames;
+ if (any instanceof User) {
+ changepwd = true;
+ resourceNames = userDAO.findAllResourceNames((User) any);
+ } else if (any instanceof AnyObject) {
+ changepwd = false;
+ resourceNames = anyObjectDAO.findAllResourceNames((AnyObject) any);
+ } else {
+ changepwd = false;
+ resourceNames = ((Group) any).getResourceNames();
+ }
+
+ List<String> noPropResources = new ArrayList<>(resourceNames);
+ noPropResources.remove(profile.getTask().getResource().getKey());
+
+ PropagationByResource propByRes = new PropagationByResource();
+ propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
+
+ taskExecutor.execute(propagationManager.getUpdateTasks(
+ any.getType().getKind(),
+ any.getKey(),
+ changepwd,
+ null,
+ propByRes,
+ null,
+ noPropResources));
+
+ return getAny(any.getKey());
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/28569df5/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AbstractSyncResultHandler.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AbstractSyncResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AbstractSyncResultHandler.java
new file mode 100644
index 0000000..42762f2
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AbstractSyncResultHandler.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.syncpull;
+
+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.persistence.api.entity.task.SyncTask;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationException;
+import org.apache.syncope.core.provisioning.api.syncpull.SyncActions;
+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.syncpull.IgnoreProvisionException;
+import org.apache.syncope.core.provisioning.api.syncpull.ProvisioningReport;
+import org.apache.syncope.core.provisioning.api.syncpull.SyncopeSyncResultHandler;
+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;
+
+@Transactional(rollbackFor = Throwable.class)
+public abstract class AbstractSyncResultHandler extends AbstractSyncopeResultHandler<SyncTask, SyncActions>
+ implements SyncopeSyncResultHandler {
+
+ @Autowired
+ protected SyncUtils syncUtilities;
+
+ @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 synchronization 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 synchronization", e);
+ return true;
+ } catch (JobExecutionException e) {
+ LOG.error("Synchronization 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("SyncTask 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 (SyncActions 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("SyncTask 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 (SyncActions 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 (SyncActions 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 synchronization 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 (SyncActions 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 (SyncActions 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("SyncTask 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 (SyncActions action : profile.getActions()) {
+ workingDelta = action.beforeUpdate(this.getProfile(), workingDelta, before, anyPatch);
+ }
+
+ AnyTO updated = doUpdate(before, anyPatch, workingDelta, result);
+
+ for (SyncActions 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 synchronization 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 (SyncActions 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 (SyncActions 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("SyncTask 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 (SyncActions action : profile.getActions()) {
+ action.beforeUnassign(this.getProfile(), delta, before);
+ }
+ } else {
+ for (SyncActions action : profile.getActions()) {
+ action.beforeDeprovision(this.getProfile(), delta, before);
+ }
+ }
+
+ doDeprovision(provision.getAnyType().getKind(), key, unlink);
+ output = getAnyTO(key);
+
+ for (SyncActions 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 synchronization 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 (SyncActions 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 (SyncActions 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("SyncTask 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 (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, unlink);
+
+ for (SyncActions 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 synchronization 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 (SyncActions 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 (SyncActions 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("SyncTask 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 (SyncActions action : profile.getActions()) {
+ workingDelta = action.beforeDelete(this.getProfile(), workingDelta, before);
+ }
+
+ try {
+ doDelete(provision.getAnyType().getKind(), key);
+ output = null;
+ resultStatus = Result.SUCCESS;
+
+ for (SyncActions 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 (SyncActions 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 synchronization 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 = syncUtilities.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.SYNCHRONIZATION,
+ getAnyUtils().getAnyTypeKind().name().toLowerCase(),
+ profile.getTask().getResource().getKey(),
+ event,
+ result,
+ before,
+ output,
+ input);
+
+ auditManager.audit(AuditElements.EventCategoryType.SYNCHRONIZATION,
+ getAnyUtils().getAnyTypeKind().name().toLowerCase(),
+ profile.getTask().getResource().getKey(),
+ event,
+ result,
+ before,
+ output,
+ input);
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/28569df5/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AbstractSyncopeResultHandler.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AbstractSyncopeResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AbstractSyncopeResultHandler.java
new file mode 100644
index 0000000..bf2cd6e
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AbstractSyncopeResultHandler.java
@@ -0,0 +1,155 @@
+/*
+ * 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.syncpull;
+
+import org.apache.syncope.common.lib.patch.AnyPatch;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask;
+import org.apache.syncope.core.provisioning.api.GroupProvisioningManager;
+import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
+import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
+import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
+import org.apache.syncope.core.provisioning.api.syncpull.ProvisioningProfile;
+import org.apache.syncope.core.provisioning.api.syncpull.SyncopeResultHandler;
+import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
+import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
+import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager;
+import org.apache.syncope.core.provisioning.api.AuditManager;
+import org.apache.syncope.core.provisioning.api.WorkflowResult;
+import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder;
+import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
+import org.apache.syncope.core.provisioning.api.syncpull.ProvisioningActions;
+import org.apache.syncope.core.workflow.api.AnyObjectWorkflowAdapter;
+import org.apache.syncope.core.workflow.api.GroupWorkflowAdapter;
+import org.apache.syncope.core.workflow.api.UserWorkflowAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public abstract class AbstractSyncopeResultHandler<T extends ProvisioningTask, A extends ProvisioningActions>
+ implements SyncopeResultHandler<T, A> {
+
+ protected static final Logger LOG = LoggerFactory.getLogger(SyncopeResultHandler.class);
+
+ @Autowired
+ protected AnyObjectDAO anyObjectDAO;
+
+ @Autowired
+ protected UserDAO userDAO;
+
+ @Autowired
+ protected GroupDAO groupDAO;
+
+ /**
+ * ConnectorObject utils.
+ */
+ @Autowired
+ protected ConnObjectUtils connObjectUtils;
+
+ /**
+ * Notification Manager.
+ */
+ @Autowired
+ protected NotificationManager notificationManager;
+
+ /**
+ * Audit Manager.
+ */
+ @Autowired
+ protected AuditManager auditManager;
+
+ /**
+ * Propagation manager.
+ */
+ @Autowired
+ protected PropagationManager propagationManager;
+
+ /**
+ * Task executor.
+ */
+ @Autowired
+ protected PropagationTaskExecutor taskExecutor;
+
+ protected AnyObjectWorkflowAdapter awfAdapter;
+
+ /**
+ * User workflow adapter.
+ */
+ @Autowired
+ protected UserWorkflowAdapter uwfAdapter;
+
+ /**
+ * Group workflow adapter.
+ */
+ @Autowired
+ protected GroupWorkflowAdapter gwfAdapter;
+
+ @Autowired
+ protected AnyObjectDataBinder anyObjectDataBinder;
+
+ @Autowired
+ protected UserDataBinder userDataBinder;
+
+ @Autowired
+ protected GroupDataBinder groupDataBinder;
+
+ @Autowired
+ protected AnyObjectProvisioningManager anyObjectProvisioningManager;
+
+ @Autowired
+ protected UserProvisioningManager userProvisioningManager;
+
+ @Autowired
+ protected GroupProvisioningManager groupProvisioningManager;
+
+ @Autowired
+ protected AnyUtilsFactory anyUtilsFactory;
+
+ /**
+ * Sync profile.
+ */
+ protected ProvisioningProfile<T, A> profile;
+
+ protected abstract AnyUtils getAnyUtils();
+
+ protected abstract AnyTO getAnyTO(long key);
+
+ protected abstract Any<?> getAny(long key);
+
+ protected abstract AnyPatch newPatch(final long key);
+
+ protected abstract WorkflowResult<Long> update(AnyPatch patch);
+
+ @Override
+ public void setProfile(final ProvisioningProfile<T, A> profile) {
+ this.profile = profile;
+ }
+
+ @Override
+ public ProvisioningProfile<T, A> getProfile() {
+ return profile;
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/28569df5/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AnyObjectPushResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AnyObjectPushResultHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AnyObjectPushResultHandlerImpl.java
new file mode 100644
index 0000000..6f83727
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AnyObjectPushResultHandlerImpl.java
@@ -0,0 +1,70 @@
+/*
+ * 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.syncpull;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.patch.AnyObjectPatch;
+import org.apache.syncope.common.lib.patch.AnyPatch;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.provisioning.api.WorkflowResult;
+import org.apache.syncope.core.provisioning.api.syncpull.AnyObjectPushResultHandler;
+
+public class AnyObjectPushResultHandlerImpl extends AbstractPushResultHandler implements AnyObjectPushResultHandler {
+
+ @Override
+ protected AnyUtils getAnyUtils() {
+ return anyUtilsFactory.getInstance(AnyTypeKind.ANY_OBJECT);
+ }
+
+ @Override
+ protected String getName(final Any<?> any) {
+ return StringUtils.EMPTY;
+ }
+
+ @Override
+ protected Any<?> getAny(final long key) {
+ try {
+ return anyObjectDAO.authFind(key);
+ } catch (Exception e) {
+ LOG.warn("Error retrieving anyObject {}", key, e);
+ return null;
+ }
+ }
+
+ @Override
+ protected AnyTO getAnyTO(final long key) {
+ return anyObjectDataBinder.getAnyObjectTO(key);
+ }
+
+ @Override
+ protected AnyPatch newPatch(final long key) {
+ AnyObjectPatch patch = new AnyObjectPatch();
+ patch.setKey(key);
+ return patch;
+ }
+
+ @Override
+ protected WorkflowResult<Long> update(final AnyPatch patch) {
+ return awfAdapter.update((AnyObjectPatch) patch);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/28569df5/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AnyObjectSyncResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AnyObjectSyncResultHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AnyObjectSyncResultHandlerImpl.java
new file mode 100644
index 0000000..a8c6c1b
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AnyObjectSyncResultHandlerImpl.java
@@ -0,0 +1,112 @@
+/*
+ * 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.syncpull;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.patch.AnyObjectPatch;
+import org.apache.syncope.common.lib.patch.AnyPatch;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.PropagationStatus;
+import org.apache.syncope.common.lib.to.AnyObjectTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.provisioning.api.ProvisioningManager;
+import org.apache.syncope.core.provisioning.api.WorkflowResult;
+import org.apache.syncope.core.provisioning.api.syncpull.ProvisioningReport;
+import org.apache.syncope.core.provisioning.api.syncpull.AnyObjectSyncResultHandler;
+import org.identityconnectors.framework.common.objects.SyncDelta;
+
+public class AnyObjectSyncResultHandlerImpl extends AbstractSyncResultHandler implements AnyObjectSyncResultHandler {
+
+ @Override
+ protected AnyUtils getAnyUtils() {
+ return anyUtilsFactory.getInstance(AnyTypeKind.ANY_OBJECT);
+ }
+
+ @Override
+ protected String getName(final AnyTO anyTO) {
+ return StringUtils.EMPTY;
+ }
+
+ @Override
+ protected ProvisioningManager<?, ?> getProvisioningManager() {
+ return anyObjectProvisioningManager;
+ }
+
+ @Override
+ protected Any<?> getAny(final long key) {
+ try {
+ return anyObjectDAO.authFind(key);
+ } catch (Exception e) {
+ LOG.warn("Error retrieving anyObject {}", key, e);
+ return null;
+ }
+ }
+
+ @Override
+ protected AnyTO getAnyTO(final long key) {
+ return anyObjectDataBinder.getAnyObjectTO(key);
+ }
+
+ @Override
+ protected AnyPatch newPatch(final long key) {
+ AnyObjectPatch patch = new AnyObjectPatch();
+ patch.setKey(key);
+ return patch;
+ }
+
+ @Override
+ protected WorkflowResult<Long> update(final AnyPatch patch) {
+ return awfAdapter.update((AnyObjectPatch) patch);
+ }
+
+ @Override
+ protected AnyTO doCreate(final AnyTO anyTO, final SyncDelta delta, final ProvisioningReport result) {
+ AnyObjectTO anyObjectTO = AnyObjectTO.class.cast(anyTO);
+
+ Map.Entry<Long, List<PropagationStatus>> created = anyObjectProvisioningManager.create(
+ anyObjectTO, Collections.singleton(profile.getTask().getResource().getKey()), true);
+
+ result.setKey(created.getKey());
+ result.setName(getName(anyTO));
+
+ return getAnyTO(created.getKey());
+ }
+
+ @Override
+ protected AnyTO doUpdate(
+ final AnyTO before,
+ final AnyPatch anyPatch,
+ final SyncDelta delta,
+ final ProvisioningReport result) {
+
+ AnyObjectPatch anyObjectPatch = AnyObjectPatch.class.cast(anyPatch);
+
+ Map.Entry<Long, List<PropagationStatus>> updated =
+ anyObjectProvisioningManager.update(anyObjectPatch, true);
+
+ AnyObjectTO after = anyObjectDataBinder.getAnyObjectTO(updated.getKey());
+ result.setName(getName(after));
+ return after;
+ }
+}