[SYNCOPE-1212] Clearing and refactoring for easier extensions
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/4661b8cc Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/4661b8cc Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/4661b8cc Branch: refs/heads/2_0_X Commit: 4661b8cc085d7b920848d67bf246b706223cb545 Parents: 8ae7057 Author: Francesco Chicchiriccò <ilgro...@apache.org> Authored: Fri Sep 22 10:12:45 2017 +0200 Committer: Francesco Chicchiriccò <ilgro...@apache.org> Committed: Fri Sep 22 10:12:45 2017 +0200 ---------------------------------------------------------------------- .../jpa/content/ContentLoaderHandler.java | 2 +- .../entity/task/AbstractProvisioningTask.java | 10 - .../api/pushpull/RealmPullResultHandler.java | 23 + .../api/pushpull/RealmPushResultHandler.java | 23 + .../java/data/TaskDataBinderImpl.java | 8 +- .../provisioning/java/job/JobManagerImpl.java | 21 +- .../pushpull/AbstractPullResultHandler.java | 14 +- .../pushpull/AbstractPushResultHandler.java | 10 +- .../AnyObjectPullResultHandlerImpl.java | 112 --- .../AnyObjectPushResultHandlerImpl.java | 70 -- .../DefaultAnyObjectPullResultHandler.java | 112 +++ .../DefaultAnyObjectPushResultHandler.java | 70 ++ .../pushpull/DefaultGroupPullResultHandler.java | 138 ++++ .../pushpull/DefaultGroupPushResultHandler.java | 70 ++ .../pushpull/DefaultRealmPullResultHandler.java | 795 +++++++++++++++++++ .../pushpull/DefaultRealmPushResultHandler.java | 449 +++++++++++ .../pushpull/DefaultUserPullResultHandler.java | 134 ++++ .../pushpull/DefaultUserPushResultHandler.java | 99 +++ .../pushpull/GroupPullResultHandlerImpl.java | 138 ---- .../pushpull/GroupPushResultHandlerImpl.java | 70 -- .../java/pushpull/PullJobDelegate.java | 87 +- .../java/pushpull/PushJobDelegate.java | 81 +- .../pushpull/RealmPullResultHandlerImpl.java | 795 ------------------- .../pushpull/RealmPushResultHandlerImpl.java | 449 ----------- .../pushpull/UserPullResultHandlerImpl.java | 134 ---- .../pushpull/UserPushResultHandlerImpl.java | 99 --- 26 files changed, 2062 insertions(+), 1951 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/syncope/blob/4661b8cc/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/ContentLoaderHandler.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/ContentLoaderHandler.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/ContentLoaderHandler.java index c287ca1..9bc30f7 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/ContentLoaderHandler.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/ContentLoaderHandler.java @@ -196,7 +196,7 @@ public class ContentLoaderHandler extends DefaultHandler { try { jdbcTemplate.update(query.toString(), getParameters(qName, atts)); } catch (DataAccessException e) { - LOG.error("While trying to perform {}", query, e); + LOG.error("While trying to perform {} with params {}", query, getParameters(qName, atts), e); if (!continueOnError) { throw e; } http://git-wip-us.apache.org/repos/asf/syncope/blob/4661b8cc/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractProvisioningTask.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractProvisioningTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractProvisioningTask.java index 0441eb3..dc35f9d 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractProvisioningTask.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractProvisioningTask.java @@ -80,16 +80,6 @@ public abstract class AbstractProvisioningTask extends JPASchedTask implements P protected MatchingRule matchingRule; @Override - public String getJobDelegateClassName() { - return null; - } - - @Override - public void setJobDelegateClassName(final String jobDelegateClassName) { - // fixed, cannot be changed - } - - @Override public ExternalResource getResource() { return resource; } http://git-wip-us.apache.org/repos/asf/syncope/blob/4661b8cc/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/RealmPullResultHandler.java ---------------------------------------------------------------------- diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/RealmPullResultHandler.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/RealmPullResultHandler.java new file mode 100644 index 0000000..6f38692 --- /dev/null +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/RealmPullResultHandler.java @@ -0,0 +1,23 @@ +/* + * 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.api.pushpull; + +public interface RealmPullResultHandler extends SyncopePullResultHandler { + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/4661b8cc/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/RealmPushResultHandler.java ---------------------------------------------------------------------- diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/RealmPushResultHandler.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/RealmPushResultHandler.java new file mode 100644 index 0000000..e994213 --- /dev/null +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/RealmPushResultHandler.java @@ -0,0 +1,23 @@ +/* + * 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.api.pushpull; + +public interface RealmPushResultHandler extends SyncopePushResultHandler { + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/4661b8cc/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 09b5d12..a22db75 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 @@ -114,7 +114,9 @@ public class TaskDataBinderImpl implements TaskDataBinder { PushTask pushTask = (PushTask) task; final PushTaskTO pushTaskTO = (PushTaskTO) taskTO; - pushTask.setJobDelegateClassName(PushJobDelegate.class.getName()); + pushTask.setJobDelegateClassName(pushTaskTO.getJobDelegateClassName() == null + ? PushJobDelegate.class.getName() + : pushTaskTO.getJobDelegateClassName()); pushTask.setSourceRealm(realmDAO.findByFullPath(pushTaskTO.getSourceRealm())); @@ -155,7 +157,9 @@ public class TaskDataBinderImpl implements TaskDataBinder { pullTask.setDestinationRealm(realmDAO.findByFullPath(pullTaskTO.getDestinationRealm())); - pullTask.setJobDelegateClassName(PullJobDelegate.class.getName()); + pullTask.setJobDelegateClassName(pullTaskTO.getJobDelegateClassName() == null + ? PullJobDelegate.class.getName() + : pullTaskTO.getJobDelegateClassName()); pullTask.setMatchingRule(pullTaskTO.getMatchingRule() == null ? MatchingRule.UPDATE : pullTaskTO.getMatchingRule()); http://git-wip-us.apache.org/repos/asf/syncope/blob/4661b8cc/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/JobManagerImpl.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/JobManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/JobManagerImpl.java index 1af62c5..b48f80c 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/JobManagerImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/JobManagerImpl.java @@ -47,8 +47,6 @@ import org.apache.syncope.core.spring.security.AuthContextUtils; import org.apache.syncope.core.spring.ApplicationContextProvider; import org.apache.syncope.core.persistence.api.SyncopeLoader; import org.apache.syncope.core.persistence.api.DomainsHolder; -import org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate; -import org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate; import org.quartz.CronScheduleBuilder; import org.quartz.Job; import org.quartz.JobBuilder; @@ -71,8 +69,11 @@ import org.identityconnectors.common.IOUtil; import org.quartz.impl.jdbcjobstore.Constants; import org.springframework.jdbc.datasource.DataSourceUtils; import org.apache.syncope.core.persistence.api.entity.task.PullTask; +import org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate; import org.apache.syncope.core.provisioning.java.job.notification.NotificationJob; import org.apache.syncope.core.provisioning.java.job.report.ReportJob; +import org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate; +import org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate; public class JobManagerImpl implements JobManager, SyncopeLoader { @@ -222,11 +223,17 @@ public class JobManagerImpl implements JobManager, SyncopeLoader { TaskJob job = createSpringBean(TaskJob.class); job.setTaskKey(task.getKey()); - String jobDelegateClassName = task instanceof PullTask - ? PullJobDelegate.class.getName() - : task instanceof PushTask - ? PushJobDelegate.class.getName() - : task.getJobDelegateClassName(); + String jobDelegateClassName = task.getJobDelegateClassName() == null + ? task instanceof PullTask + ? PullJobDelegate.class.getName() + : task instanceof PushTask + ? PushJobDelegate.class.getName() + : null + : task.getJobDelegateClassName(); + if (jobDelegateClassName == null) { + throw new IllegalArgumentException("Task " + task + + " does not provide any " + SchedTaskJobDelegate.class.getSimpleName()); + } Map<String, Object> jobMap = new HashMap<>(); jobMap.put(JobManager.DOMAIN_KEY, AuthContextUtils.getDomain()); http://git-wip-us.apache.org/repos/asf/syncope/blob/4661b8cc/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 index 6b6f922..6c54260 100644 --- 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 @@ -69,23 +69,23 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan protected PullUtils pullUtils; @Autowired - private NotificationManager notificationManager; + protected NotificationManager notificationManager; @Autowired - private AuditManager auditManager; + protected AuditManager auditManager; @Autowired - private ConnObjectUtils connObjectUtils; + protected ConnObjectUtils connObjectUtils; @Autowired - private VirSchemaDAO virSchemaDAO; + protected VirSchemaDAO virSchemaDAO; @Autowired - private VirAttrCache virAttrCache; + protected VirAttrCache virAttrCache; - private SyncopePullExecutor executor; + protected SyncopePullExecutor executor; - private Result latestResult; + protected Result latestResult; protected abstract String getName(AnyTO anyTO); http://git-wip-us.apache.org/repos/asf/syncope/blob/4661b8cc/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java index 7095739..6b03c1d 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java @@ -95,7 +95,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan } } - private void update(final Any<?> any, final ProvisioningReport result) { + protected void update(final Any<?> any, final ProvisioningReport result) { boolean changepwd; Collection<String> resourceKeys; if (any instanceof User) { @@ -240,7 +240,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan } } - private void doHandle(final Any<?> any) throws JobExecutionException { + protected void doHandle(final Any<?> any) throws JobExecutionException { AnyUtils anyUtils = anyUtilsFactory.getInstance(any); ProvisioningReport result = new ProvisioningReport(); @@ -472,7 +472,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan } } - private ResourceOperation toResourceOperation(final UnmatchingRule rule) { + protected ResourceOperation toResourceOperation(final UnmatchingRule rule) { switch (rule) { case ASSIGN: case PROVISION: @@ -482,7 +482,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan } } - private ResourceOperation toResourceOperation(final MatchingRule rule) { + protected ResourceOperation toResourceOperation(final MatchingRule rule) { switch (rule) { case UPDATE: return ResourceOperation.UPDATE; @@ -494,7 +494,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan } } - private ProvisioningReport.Status toProvisioningReportStatus(final PropagationTaskExecStatus status) { + protected ProvisioningReport.Status toProvisioningReportStatus(final PropagationTaskExecStatus status) { switch (status) { case FAILURE: return ProvisioningReport.Status.FAILURE; http://git-wip-us.apache.org/repos/asf/syncope/blob/4661b8cc/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java deleted file mode 100644 index 3e652ee..0000000 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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.Collections; -import java.util.List; -import java.util.Map; -import org.apache.commons.lang3.tuple.Pair; -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.AnyObjectProvisioningManager; -import org.apache.syncope.core.provisioning.api.ProvisioningManager; -import org.apache.syncope.core.provisioning.api.WorkflowResult; -import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport; -import org.identityconnectors.framework.common.objects.SyncDelta; -import org.apache.syncope.core.provisioning.api.pushpull.AnyObjectPullResultHandler; -import org.springframework.beans.factory.annotation.Autowired; - -public class AnyObjectPullResultHandlerImpl extends AbstractPullResultHandler implements AnyObjectPullResultHandler { - - @Autowired - private AnyObjectProvisioningManager anyObjectProvisioningManager; - - @Override - protected AnyUtils getAnyUtils() { - return anyUtilsFactory.getInstance(AnyTypeKind.ANY_OBJECT); - } - - @Override - protected String getName(final AnyTO anyTO) { - return AnyObjectTO.class.cast(anyTO).getName(); - } - - @Override - protected ProvisioningManager<?, ?> getProvisioningManager() { - return anyObjectProvisioningManager; - } - - @Override - protected Any<?> getAny(final String key) { - try { - return anyObjectDAO.authFind(key); - } catch (Exception e) { - LOG.warn("Error retrieving anyObject {}", key, e); - return null; - } - } - - @Override - protected AnyTO getAnyTO(final String key) { - return anyObjectDataBinder.getAnyObjectTO(key); - } - - @Override - protected AnyPatch newPatch(final String key) { - AnyObjectPatch patch = new AnyObjectPatch(); - patch.setKey(key); - return patch; - } - - @Override - protected WorkflowResult<? extends AnyPatch> update(final AnyPatch patch) { - return awfAdapter.update((AnyObjectPatch) patch); - } - - @Override - protected AnyTO doCreate(final AnyTO anyTO, final SyncDelta delta) { - AnyObjectTO anyObjectTO = AnyObjectTO.class.cast(anyTO); - - Map.Entry<String, List<PropagationStatus>> created = anyObjectProvisioningManager.create( - anyObjectTO, Collections.singleton(profile.getTask().getResource().getKey()), true); - - return getAnyTO(created.getKey()); - } - - @Override - protected AnyPatch doUpdate( - final AnyTO before, - final AnyPatch anyPatch, - final SyncDelta delta, - final ProvisioningReport result) { - - AnyObjectPatch anyObjectPatch = AnyObjectPatch.class.cast(anyPatch); - - Pair<AnyObjectPatch, List<PropagationStatus>> updated = anyObjectProvisioningManager.update( - anyObjectPatch, Collections.singleton(profile.getTask().getResource().getKey()), true); - - return anyPatch; - } -} http://git-wip-us.apache.org/repos/asf/syncope/blob/4661b8cc/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPushResultHandlerImpl.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPushResultHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPushResultHandlerImpl.java deleted file mode 100644 index 48f464a..0000000 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPushResultHandlerImpl.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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 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.pushpull.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 String key) { - try { - return anyObjectDAO.authFind(key); - } catch (Exception e) { - LOG.warn("Error retrieving anyObject {}", key, e); - return null; - } - } - - @Override - protected AnyTO getAnyTO(final String key) { - return anyObjectDataBinder.getAnyObjectTO(key); - } - - @Override - protected AnyPatch newPatch(final String key) { - AnyObjectPatch patch = new AnyObjectPatch(); - patch.setKey(key); - return patch; - } - - @Override - protected WorkflowResult<? extends AnyObjectPatch> update(final AnyPatch patch) { - return awfAdapter.update((AnyObjectPatch) patch); - } - -} http://git-wip-us.apache.org/repos/asf/syncope/blob/4661b8cc/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultAnyObjectPullResultHandler.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultAnyObjectPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultAnyObjectPullResultHandler.java new file mode 100644 index 0000000..8d71a2d --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultAnyObjectPullResultHandler.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.pushpull; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.apache.commons.lang3.tuple.Pair; +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.AnyObjectProvisioningManager; +import org.apache.syncope.core.provisioning.api.ProvisioningManager; +import org.apache.syncope.core.provisioning.api.WorkflowResult; +import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport; +import org.identityconnectors.framework.common.objects.SyncDelta; +import org.apache.syncope.core.provisioning.api.pushpull.AnyObjectPullResultHandler; +import org.springframework.beans.factory.annotation.Autowired; + +public class DefaultAnyObjectPullResultHandler extends AbstractPullResultHandler implements AnyObjectPullResultHandler { + + @Autowired + private AnyObjectProvisioningManager anyObjectProvisioningManager; + + @Override + protected AnyUtils getAnyUtils() { + return anyUtilsFactory.getInstance(AnyTypeKind.ANY_OBJECT); + } + + @Override + protected String getName(final AnyTO anyTO) { + return AnyObjectTO.class.cast(anyTO).getName(); + } + + @Override + protected ProvisioningManager<?, ?> getProvisioningManager() { + return anyObjectProvisioningManager; + } + + @Override + protected Any<?> getAny(final String key) { + try { + return anyObjectDAO.authFind(key); + } catch (Exception e) { + LOG.warn("Error retrieving anyObject {}", key, e); + return null; + } + } + + @Override + protected AnyTO getAnyTO(final String key) { + return anyObjectDataBinder.getAnyObjectTO(key); + } + + @Override + protected AnyPatch newPatch(final String key) { + AnyObjectPatch patch = new AnyObjectPatch(); + patch.setKey(key); + return patch; + } + + @Override + protected WorkflowResult<? extends AnyPatch> update(final AnyPatch patch) { + return awfAdapter.update((AnyObjectPatch) patch); + } + + @Override + protected AnyTO doCreate(final AnyTO anyTO, final SyncDelta delta) { + AnyObjectTO anyObjectTO = AnyObjectTO.class.cast(anyTO); + + Map.Entry<String, List<PropagationStatus>> created = anyObjectProvisioningManager.create( + anyObjectTO, Collections.singleton(profile.getTask().getResource().getKey()), true); + + return getAnyTO(created.getKey()); + } + + @Override + protected AnyPatch doUpdate( + final AnyTO before, + final AnyPatch anyPatch, + final SyncDelta delta, + final ProvisioningReport result) { + + AnyObjectPatch anyObjectPatch = AnyObjectPatch.class.cast(anyPatch); + + Pair<AnyObjectPatch, List<PropagationStatus>> updated = anyObjectProvisioningManager.update( + anyObjectPatch, Collections.singleton(profile.getTask().getResource().getKey()), true); + + return anyPatch; + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/4661b8cc/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultAnyObjectPushResultHandler.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultAnyObjectPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultAnyObjectPushResultHandler.java new file mode 100644 index 0000000..3c401ae --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultAnyObjectPushResultHandler.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.pushpull; + +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.pushpull.AnyObjectPushResultHandler; + +public class DefaultAnyObjectPushResultHandler 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 String key) { + try { + return anyObjectDAO.authFind(key); + } catch (Exception e) { + LOG.warn("Error retrieving anyObject {}", key, e); + return null; + } + } + + @Override + protected AnyTO getAnyTO(final String key) { + return anyObjectDataBinder.getAnyObjectTO(key); + } + + @Override + protected AnyPatch newPatch(final String key) { + AnyObjectPatch patch = new AnyObjectPatch(); + patch.setKey(key); + return patch; + } + + @Override + protected WorkflowResult<? extends AnyObjectPatch> update(final AnyPatch patch) { + return awfAdapter.update((AnyObjectPatch) patch); + } + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/4661b8cc/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultGroupPullResultHandler.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultGroupPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultGroupPullResultHandler.java new file mode 100644 index 0000000..451659b --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultGroupPullResultHandler.java @@ -0,0 +1,138 @@ +/* + * 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.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.common.lib.patch.AnyPatch; +import org.apache.syncope.common.lib.patch.AttrPatch; +import org.apache.syncope.common.lib.patch.GroupPatch; +import org.apache.syncope.common.lib.to.AnyTO; +import org.apache.syncope.common.lib.to.PropagationStatus; +import org.apache.syncope.common.lib.to.GroupTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.PatchOperation; +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.GroupProvisioningManager; +import org.apache.syncope.core.provisioning.api.ProvisioningManager; +import org.apache.syncope.core.provisioning.api.WorkflowResult; +import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport; +import org.identityconnectors.framework.common.objects.SyncDelta; +import org.apache.syncope.core.provisioning.api.pushpull.GroupPullResultHandler; +import org.springframework.beans.factory.annotation.Autowired; + +public class DefaultGroupPullResultHandler extends AbstractPullResultHandler implements GroupPullResultHandler { + + @Autowired + private GroupProvisioningManager groupProvisioningManager; + + private final Map<String, String> groupOwnerMap = new HashMap<>(); + + @Override + public Map<String, String> getGroupOwnerMap() { + return this.groupOwnerMap; + } + + @Override + protected AnyUtils getAnyUtils() { + return anyUtilsFactory.getInstance(AnyTypeKind.GROUP); + } + + @Override + protected String getName(final AnyTO anyTO) { + return GroupTO.class.cast(anyTO).getName(); + } + + @Override + protected ProvisioningManager<?, ?> getProvisioningManager() { + return groupProvisioningManager; + } + + @Override + protected Any<?> getAny(final String key) { + try { + return groupDAO.authFind(key); + } catch (Exception e) { + LOG.warn("Error retrieving group {}", key, e); + return null; + } + } + + @Override + protected AnyTO getAnyTO(final String key) { + return groupDataBinder.getGroupTO(key); + } + + @Override + protected AnyPatch newPatch(final String key) { + GroupPatch patch = new GroupPatch(); + patch.setKey(key); + return patch; + } + + @Override + protected WorkflowResult<? extends AnyPatch> update(final AnyPatch patch) { + return gwfAdapter.update((GroupPatch) patch); + } + + @Override + protected AnyTO doCreate(final AnyTO anyTO, final SyncDelta delta) { + GroupTO groupTO = GroupTO.class.cast(anyTO); + + Map.Entry<String, List<PropagationStatus>> created = groupProvisioningManager.create( + groupTO, + groupOwnerMap, + Collections.singleton(profile.getTask().getResource().getKey()), + true); + + return getAnyTO(created.getKey()); + } + + @Override + protected AnyPatch doUpdate( + final AnyTO before, + final AnyPatch anyPatch, + final SyncDelta delta, + final ProvisioningReport result) { + + GroupPatch groupPatch = GroupPatch.class.cast(anyPatch); + + Pair<GroupPatch, List<PropagationStatus>> updated = groupProvisioningManager.update( + groupPatch, Collections.singleton(profile.getTask().getResource().getKey()), true); + + String groupOwner = null; + for (AttrPatch attrPatch : groupPatch.getPlainAttrs()) { + if (attrPatch.getOperation() == PatchOperation.ADD_REPLACE && attrPatch.getAttrTO() != null + && attrPatch.getAttrTO().getSchema().isEmpty() && !attrPatch.getAttrTO().getValues().isEmpty()) { + + groupOwner = attrPatch.getAttrTO().getValues().get(0); + } + } + if (groupOwner != null) { + groupOwnerMap.put(updated.getLeft().getKey(), groupOwner); + } + + return anyPatch; + } + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/4661b8cc/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultGroupPushResultHandler.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultGroupPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultGroupPushResultHandler.java new file mode 100644 index 0000000..993f747 --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultGroupPushResultHandler.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.pushpull; + +import org.apache.syncope.common.lib.patch.GroupPatch; +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.persistence.api.entity.group.Group; +import org.apache.syncope.core.provisioning.api.WorkflowResult; +import org.apache.syncope.core.provisioning.api.pushpull.GroupPushResultHandler; + +public class DefaultGroupPushResultHandler extends AbstractPushResultHandler implements GroupPushResultHandler { + + @Override + protected AnyUtils getAnyUtils() { + return anyUtilsFactory.getInstance(AnyTypeKind.GROUP); + } + + @Override + protected String getName(final Any<?> any) { + return Group.class.cast(any).getName(); + } + + @Override + protected Any<?> getAny(final String key) { + try { + return groupDAO.authFind(key); + } catch (Exception e) { + LOG.warn("Error retrieving group {}", key, e); + return null; + } + } + + @Override + protected AnyTO getAnyTO(final String key) { + return groupDataBinder.getGroupTO(key); + } + + @Override + protected AnyPatch newPatch(final String key) { + GroupPatch patch = new GroupPatch(); + patch.setKey(key); + return patch; + } + + @Override + protected WorkflowResult<? extends AnyPatch> update(final AnyPatch patch) { + return gwfAdapter.update((GroupPatch) patch); + } + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/4661b8cc/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java new file mode 100644 index 0000000..69c0604 --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java @@ -0,0 +1,795 @@ +/* + * 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 java.util.Set; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.RealmTO; +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.ClientExceptionType; +import org.apache.syncope.common.lib.types.MatchingRule; +import org.apache.syncope.core.provisioning.api.PropagationByResource; +import org.apache.syncope.common.lib.types.PullMode; +import org.apache.syncope.common.lib.types.ResourceOperation; +import org.apache.syncope.common.lib.types.UnmatchingRule; +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; +import org.apache.syncope.core.persistence.api.dao.search.AnyCond; +import org.apache.syncope.core.persistence.api.dao.search.AttributeCond; +import org.apache.syncope.core.persistence.api.dao.search.SearchCond; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit; +import org.apache.syncope.core.persistence.api.entity.task.PropagationTask; +import org.apache.syncope.core.persistence.api.entity.task.PullTask; +import org.apache.syncope.core.provisioning.api.propagation.PropagationException; +import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException; +import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport; +import org.apache.syncope.core.provisioning.api.pushpull.PullActions; +import org.apache.syncope.core.provisioning.api.pushpull.RealmPullResultHandler; +import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullExecutor; +import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils; +import org.apache.syncope.core.spring.security.DelegatedAdministrationException; +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 class DefaultRealmPullResultHandler + extends AbstractRealmResultHandler<PullTask, PullActions> + implements RealmPullResultHandler { + + @Autowired + private PullUtils pullUtils; + + @Autowired + private ConnObjectUtils connObjectUtils; + + @Autowired + private AnySearchDAO searchDAO; + + private SyncopePullExecutor executor; + + private Result latestResult; + + @Override + public void setPullExecutor(final SyncopePullExecutor executor) { + this.executor = executor; + } + + @Override + public boolean handle(final SyncDelta delta) { + try { + OrgUnit orgUnit = profile.getTask().getResource().getOrgUnit(); + if (orgUnit == null) { + throw new JobExecutionException("No orgUnit found on " + profile.getTask().getResource() + " for " + + delta.getObject().getObjectClass()); + } + + doHandle(delta, orgUnit); + + LOG.debug("Successfully handled {}", delta); + + if (profile.getTask().getPullMode() != PullMode.INCREMENTAL) { + return true; + } + + boolean shouldContinue; + synchronized (this) { + shouldContinue = latestResult == Result.SUCCESS; + this.latestResult = null; + } + if (shouldContinue) { + executor.setLatestSyncToken(delta.getObjectClass(), delta.getToken()); + } + return shouldContinue; + } catch (IgnoreProvisionException e) { + ProvisioningReport ignoreResult = new ProvisioningReport(); + ignoreResult.setOperation(ResourceOperation.NONE); + ignoreResult.setStatus(ProvisioningReport.Status.IGNORE); + ignoreResult.setAnyType(REALM_TYPE); + ignoreResult.setKey(null); + ignoreResult.setName(delta.getObject().getName().getNameValue()); + profile.getResults().add(ignoreResult); + + LOG.warn("Ignoring during pull", e); + + executor.setLatestSyncToken(delta.getObjectClass(), delta.getToken()); + + return true; + } catch (JobExecutionException e) { + LOG.error("Pull failed", e); + + return false; + } + } + + private List<ProvisioningReport> assign(final SyncDelta delta, final OrgUnit orgUnit) throws JobExecutionException { + if (!profile.getTask().isPerformCreate()) { + LOG.debug("PullTask not configured for create"); + finalize(UnmatchingRule.toEventName(UnmatchingRule.ASSIGN), Result.SUCCESS, null, null, delta); + return Collections.<ProvisioningReport>emptyList(); + } + + RealmTO realmTO = connObjectUtils.getRealmTO(delta.getObject(), profile.getTask(), orgUnit); + if (realmTO.getFullPath() == null) { + if (realmTO.getParent() == null) { + realmTO.setParent(profile.getTask().getDestinatioRealm().getFullPath()); + } + + realmTO.setFullPath(realmTO.getParent() + "/" + realmTO.getName()); + } + realmTO.getResources().add(profile.getTask().getResource().getKey()); + + ProvisioningReport result = new ProvisioningReport(); + result.setOperation(ResourceOperation.CREATE); + result.setAnyType(REALM_TYPE); + result.setStatus(ProvisioningReport.Status.SUCCESS); + result.setName(realmTO.getFullPath()); + + if (profile.isDryRun()) { + result.setKey(null); + finalize(UnmatchingRule.toEventName(UnmatchingRule.ASSIGN), Result.SUCCESS, null, null, delta); + } else { + SyncDelta actionedDelta = delta; + for (PullActions action : profile.getActions()) { + actionedDelta = action.beforeAssign(profile, actionedDelta, realmTO); + } + + create(realmTO, actionedDelta, UnmatchingRule.toEventName(UnmatchingRule.ASSIGN), result); + } + + return Collections.singletonList(result); + } + + private List<ProvisioningReport> provision(final SyncDelta delta, final OrgUnit orgUnit) + throws JobExecutionException { + + if (!profile.getTask().isPerformCreate()) { + LOG.debug("PullTask not configured for create"); + finalize(UnmatchingRule.toEventName(UnmatchingRule.PROVISION), Result.SUCCESS, null, null, delta); + return Collections.<ProvisioningReport>emptyList(); + } + + RealmTO realmTO = connObjectUtils.getRealmTO(delta.getObject(), profile.getTask(), orgUnit); + if (realmTO.getFullPath() == null) { + if (realmTO.getParent() == null) { + realmTO.setParent(profile.getTask().getDestinatioRealm().getFullPath()); + } + + realmTO.setFullPath(realmTO.getParent() + "/" + realmTO.getName()); + } + + ProvisioningReport result = new ProvisioningReport(); + result.setOperation(ResourceOperation.CREATE); + result.setAnyType(REALM_TYPE); + result.setStatus(ProvisioningReport.Status.SUCCESS); + result.setName(realmTO.getFullPath()); + + if (profile.isDryRun()) { + result.setKey(null); + finalize(UnmatchingRule.toEventName(UnmatchingRule.PROVISION), Result.SUCCESS, null, null, delta); + } else { + SyncDelta actionedDelta = delta; + for (PullActions action : profile.getActions()) { + actionedDelta = action.beforeProvision(profile, actionedDelta, realmTO); + } + + create(realmTO, actionedDelta, UnmatchingRule.toEventName(UnmatchingRule.PROVISION), result); + } + + return Collections.singletonList(result); + } + + private void throwIgnoreProvisionException(final SyncDelta delta, final Exception exception) + throws JobExecutionException { + + if (exception instanceof IgnoreProvisionException) { + throw IgnoreProvisionException.class.cast(exception); + } + + IgnoreProvisionException ipe = null; + for (PullActions action : profile.getActions()) { + if (ipe == null) { + ipe = action.onError(profile, delta, exception); + } + } + if (ipe != null) { + throw ipe; + } + } + + private void create( + final RealmTO realmTO, + final SyncDelta delta, + final String operation, + final ProvisioningReport result) + throws JobExecutionException { + + Object output; + Result resultStatus; + + try { + Realm realm = realmDAO.save(binder.create(profile.getTask().getDestinatioRealm().getFullPath(), realmTO)); + + PropagationByResource propByRes = new PropagationByResource(); + for (String resource : realm.getResourceKeys()) { + propByRes.add(ResourceOperation.CREATE, resource); + } + List<PropagationTask> tasks = propagationManager.createTasks(realm, propByRes, null); + taskExecutor.execute(tasks, false); + + RealmTO actual = binder.getRealmTO(realm, true); + + result.setKey(actual.getKey()); + result.setName(profile.getTask().getDestinatioRealm().getFullPath() + "/" + actual.getName()); + + output = actual; + resultStatus = Result.SUCCESS; + + for (PullActions action : profile.getActions()) { + action.after(profile, delta, actual, result); + } + + LOG.debug("Realm {} successfully created", actual.getKey()); + } 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 Realm {}", delta.getUid().getUidValue(), e); + output = e; + resultStatus = Result.FAILURE; + } catch (Exception e) { + throwIgnoreProvisionException(delta, e); + + result.setStatus(ProvisioningReport.Status.FAILURE); + result.setMessage(ExceptionUtils.getRootCauseMessage(e)); + LOG.error("Could not create Realm {} ", delta.getUid().getUidValue(), e); + output = e; + resultStatus = Result.FAILURE; + } + + finalize(operation, resultStatus, null, output, delta); + } + + private List<ProvisioningReport> update(final SyncDelta delta, final List<String> keys) + throws JobExecutionException { + + if (!profile.getTask().isPerformUpdate()) { + LOG.debug("PullTask not configured for update"); + finalize(MatchingRule.toEventName(MatchingRule.UPDATE), Result.SUCCESS, null, null, delta); + return Collections.<ProvisioningReport>emptyList(); + } + + LOG.debug("About to update {}", keys); + + List<ProvisioningReport> results = new ArrayList<>(); + + SyncDelta workingDelta = delta; + for (String key : keys) { + LOG.debug("About to update {}", key); + + ProvisioningReport result = new ProvisioningReport(); + result.setOperation(ResourceOperation.UPDATE); + result.setAnyType(REALM_TYPE); + result.setStatus(ProvisioningReport.Status.SUCCESS); + result.setKey(key); + + Realm realm = realmDAO.find(key); + RealmTO before = binder.getRealmTO(realm, true); + if (before == null) { + result.setStatus(ProvisioningReport.Status.FAILURE); + result.setMessage(String.format("Realm '%s' not found", key)); + } else { + result.setName(before.getFullPath()); + } + + if (!profile.isDryRun()) { + Result resultStatus; + Object output; + + if (before == null) { + resultStatus = Result.FAILURE; + output = null; + } else { + try { + for (PullActions action : profile.getActions()) { + workingDelta = action.beforeUpdate(profile, workingDelta, before, null); + } + + PropagationByResource propByRes = binder.update(realm, before); + realm = realmDAO.save(realm); + RealmTO updated = binder.getRealmTO(realm, true); + + List<PropagationTask> tasks = propagationManager.createTasks(realm, propByRes, null); + taskExecutor.execute(tasks, false); + + for (PullActions action : profile.getActions()) { + action.after(profile, workingDelta, updated, result); + } + + output = updated; + resultStatus = Result.SUCCESS; + result.setName(updated.getFullPath()); + + LOG.debug("{} successfully updated", updated); + } 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 Realm {}", workingDelta.getUid().getUidValue(), e); + output = e; + resultStatus = Result.FAILURE; + } catch (Exception e) { + throwIgnoreProvisionException(workingDelta, e); + + result.setStatus(ProvisioningReport.Status.FAILURE); + result.setMessage(ExceptionUtils.getRootCauseMessage(e)); + LOG.error("Could not update Realm {}", workingDelta.getUid().getUidValue(), e); + output = e; + resultStatus = Result.FAILURE; + } + } + finalize(MatchingRule.toEventName(MatchingRule.UPDATE), resultStatus, before, output, workingDelta); + } + results.add(result); + } + + return results; + } + + private List<ProvisioningReport> deprovision(final SyncDelta delta, final List<String> keys, final boolean unlink) + throws JobExecutionException { + + if (!profile.getTask().isPerformUpdate()) { + LOG.debug("PullTask not configured for update"); + finalize(unlink + ? MatchingRule.toEventName(MatchingRule.UNASSIGN) + : MatchingRule.toEventName(MatchingRule.DEPROVISION), Result.SUCCESS, null, null, delta); + return Collections.<ProvisioningReport>emptyList(); + } + + LOG.debug("About to deprovision {}", keys); + + final List<ProvisioningReport> results = new ArrayList<>(); + + SyncDelta workingDelta = delta; + for (String key : keys) { + LOG.debug("About to unassign resource {}", key); + + ProvisioningReport result = new ProvisioningReport(); + result.setOperation(ResourceOperation.DELETE); + result.setAnyType(REALM_TYPE); + result.setStatus(ProvisioningReport.Status.SUCCESS); + result.setKey(key); + + Realm realm = realmDAO.find(key); + RealmTO before = binder.getRealmTO(realm, true); + if (before == null) { + result.setStatus(ProvisioningReport.Status.FAILURE); + result.setMessage(String.format("Realm '%s' not found", key)); + } else { + result.setName(before.getFullPath()); + } + + if (!profile.isDryRun()) { + Object output; + Result resultStatus; + + if (before == null) { + resultStatus = Result.FAILURE; + output = null; + } else { + try { + if (unlink) { + for (PullActions action : profile.getActions()) { + workingDelta = action.beforeUnassign(profile, workingDelta, before); + } + } else { + for (PullActions action : profile.getActions()) { + workingDelta = action.beforeDeprovision(profile, workingDelta, before); + } + } + + PropagationByResource propByRes = new PropagationByResource(); + propByRes.add(ResourceOperation.DELETE, profile.getTask().getResource().getKey()); + taskExecutor.execute(propagationManager.createTasks(realm, propByRes, null), false); + + if (unlink) { + realm.getResources().remove(profile.getTask().getResource()); + output = binder.getRealmTO(realmDAO.save(realm), true); + } else { + output = binder.getRealmTO(realm, true); + } + + for (PullActions action : profile.getActions()) { + action.after(profile, workingDelta, RealmTO.class.cast(output), result); + } + + resultStatus = Result.SUCCESS; + + LOG.debug("{} successfully updated", realm); + } 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 Realm {}", workingDelta.getUid().getUidValue(), e); + output = e; + resultStatus = Result.FAILURE; + } catch (Exception e) { + throwIgnoreProvisionException(workingDelta, e); + + result.setStatus(ProvisioningReport.Status.FAILURE); + result.setMessage(ExceptionUtils.getRootCauseMessage(e)); + LOG.error("Could not update Realm {}", delta.getUid().getUidValue(), e); + output = e; + resultStatus = Result.FAILURE; + } + } + finalize(unlink + ? MatchingRule.toEventName(MatchingRule.UNASSIGN) + : MatchingRule.toEventName(MatchingRule.DEPROVISION), resultStatus, before, output, delta); + } + results.add(result); + } + + return results; + } + + private List<ProvisioningReport> link(final SyncDelta delta, final List<String> keys, final boolean unlink) + throws JobExecutionException { + + if (!profile.getTask().isPerformUpdate()) { + LOG.debug("PullTask not configured for update"); + finalize(unlink + ? MatchingRule.toEventName(MatchingRule.UNLINK) + : MatchingRule.toEventName(MatchingRule.LINK), Result.SUCCESS, null, null, delta); + return Collections.<ProvisioningReport>emptyList(); + } + + LOG.debug("About to link {}", keys); + + final List<ProvisioningReport> results = new ArrayList<>(); + + SyncDelta workingDelta = delta; + for (String key : keys) { + LOG.debug("About to unassign resource {}", key); + + ProvisioningReport result = new ProvisioningReport(); + result.setOperation(ResourceOperation.NONE); + result.setAnyType(REALM_TYPE); + result.setStatus(ProvisioningReport.Status.SUCCESS); + result.setKey(key); + + Realm realm = realmDAO.find(key); + RealmTO before = binder.getRealmTO(realm, true); + if (before == null) { + result.setStatus(ProvisioningReport.Status.FAILURE); + result.setMessage(String.format("Realm '%s' not found", key)); + } else { + result.setName(before.getFullPath()); + } + + Object output; + Result resultStatus; + if (!profile.isDryRun()) { + if (before == null) { + resultStatus = Result.FAILURE; + output = null; + } else { + try { + if (unlink) { + for (PullActions action : profile.getActions()) { + workingDelta = action.beforeUnlink(profile, workingDelta, before); + } + } else { + for (PullActions action : profile.getActions()) { + workingDelta = action.beforeLink(profile, workingDelta, before); + } + } + + if (unlink) { + realm.getResources().remove(profile.getTask().getResource()); + } else { + realm.add(profile.getTask().getResource()); + } + output = update(workingDelta, Collections.singletonList(key)); + + for (PullActions action : profile.getActions()) { + action.after(profile, workingDelta, RealmTO.class.cast(output), result); + } + + resultStatus = Result.SUCCESS; + + LOG.debug("{} successfully updated", realm); + } 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 Realm {}", workingDelta.getUid().getUidValue(), e); + output = e; + resultStatus = Result.FAILURE; + } catch (Exception e) { + throwIgnoreProvisionException(workingDelta, e); + + result.setStatus(ProvisioningReport.Status.FAILURE); + result.setMessage(ExceptionUtils.getRootCauseMessage(e)); + LOG.error("Could not update Realm {}", workingDelta.getUid().getUidValue(), e); + output = e; + resultStatus = Result.FAILURE; + } + } + finalize(unlink + ? MatchingRule.toEventName(MatchingRule.UNLINK) + : MatchingRule.toEventName(MatchingRule.LINK), resultStatus, before, output, workingDelta); + } + results.add(result); + } + + return results; + } + + private List<ProvisioningReport> delete(final SyncDelta delta, final List<String> keys) + throws JobExecutionException { + + if (!profile.getTask().isPerformDelete()) { + LOG.debug("PullTask not configured for delete"); + finalize(ResourceOperation.DELETE.name().toLowerCase(), Result.SUCCESS, null, null, delta); + return Collections.<ProvisioningReport>emptyList(); + } + + LOG.debug("About to delete {}", keys); + + List<ProvisioningReport> results = new ArrayList<>(); + + SyncDelta workingDelta = delta; + for (String key : keys) { + Object output; + Result resultStatus = Result.FAILURE; + + ProvisioningReport result = new ProvisioningReport(); + + try { + result.setKey(key); + result.setOperation(ResourceOperation.DELETE); + result.setAnyType(REALM_TYPE); + result.setStatus(ProvisioningReport.Status.SUCCESS); + + Realm realm = realmDAO.find(key); + RealmTO before = binder.getRealmTO(realm, true); + if (before == null) { + result.setStatus(ProvisioningReport.Status.FAILURE); + result.setMessage(String.format("Realm '%s' not found", key)); + } else { + result.setName(before.getFullPath()); + } + + if (!profile.isDryRun()) { + for (PullActions action : profile.getActions()) { + workingDelta = action.beforeDelete(profile, workingDelta, before); + } + + try { + if (!realmDAO.findChildren(realm).isEmpty()) { + throw SyncopeClientException.build(ClientExceptionType.HasChildren); + } + + Set<String> adminRealms = Collections.singleton(realm.getFullPath()); + AnyCond keyCond = new AnyCond(AttributeCond.Type.ISNOTNULL); + keyCond.setSchema("key"); + SearchCond allMatchingCond = SearchCond.getLeafCond(keyCond); + int users = searchDAO.count(adminRealms, allMatchingCond, AnyTypeKind.USER); + int groups = searchDAO.count(adminRealms, allMatchingCond, AnyTypeKind.GROUP); + int anyObjects = searchDAO.count(adminRealms, allMatchingCond, AnyTypeKind.ANY_OBJECT); + + if (users + groups + anyObjects > 0) { + SyncopeClientException containedAnys = SyncopeClientException.build( + ClientExceptionType.AssociatedAnys); + containedAnys.getElements().add(users + " user(s)"); + containedAnys.getElements().add(groups + " group(s)"); + containedAnys.getElements().add(anyObjects + " anyObject(s)"); + throw containedAnys; + } + + PropagationByResource propByRes = new PropagationByResource(); + for (String resource : realm.getResourceKeys()) { + propByRes.add(ResourceOperation.DELETE, resource); + } + List<PropagationTask> tasks = propagationManager.createTasks(realm, propByRes, null); + taskExecutor.execute(tasks, false); + + realmDAO.delete(realm); + + output = null; + resultStatus = Result.SUCCESS; + + for (PullActions action : profile.getActions()) { + action.after(profile, workingDelta, before, result); + } + } catch (Exception e) { + throwIgnoreProvisionException(workingDelta, e); + + result.setStatus(ProvisioningReport.Status.FAILURE); + result.setMessage(ExceptionUtils.getRootCauseMessage(e)); + LOG.error("Could not delete {}", realm, e); + output = e; + } + + finalize(ResourceOperation.DELETE.name().toLowerCase(), resultStatus, before, output, workingDelta); + } + + results.add(result); + } catch (DelegatedAdministrationException e) { + LOG.error("Not allowed to read Realm {}", key, e); + } catch (Exception e) { + LOG.error("Could not delete Realm {}", key, e); + } + } + + return results; + } + + private ProvisioningReport ignore( + final SyncDelta delta, + final boolean matching) + throws JobExecutionException { + + LOG.debug("Any to ignore {}", delta.getObject().getUid().getUidValue()); + + ProvisioningReport result = new ProvisioningReport(); + + result.setKey(null); + result.setName(delta.getObject().getUid().getUidValue()); + result.setOperation(ResourceOperation.NONE); + result.setAnyType(REALM_TYPE); + result.setStatus(ProvisioningReport.Status.SUCCESS); + + if (!profile.isDryRun()) { + finalize(matching + ? MatchingRule.toEventName(MatchingRule.IGNORE) + : UnmatchingRule.toEventName(UnmatchingRule.IGNORE), Result.SUCCESS, null, null, delta); + } + + return result; + } + + private void doHandle(final SyncDelta delta, final OrgUnit orgUnit) throws JobExecutionException { + LOG.debug("Process {} for {} as {}", + delta.getDeltaType(), delta.getUid().getUidValue(), delta.getObject().getObjectClass()); + + String uid = delta.getPreviousUid() == null + ? delta.getUid().getUidValue() + : delta.getPreviousUid().getUidValue(); + + List<String> keys = pullUtils.findExisting(uid, delta.getObject(), orgUnit); + LOG.debug("Match found for {} as {}: {}", + delta.getUid().getUidValue(), delta.getObject().getObjectClass(), keys); + + if (keys.size() > 1) { + switch (profile.getResAct()) { + case IGNORE: + throw new IllegalStateException("More than one match " + keys); + + case FIRSTMATCH: + keys = keys.subList(0, 1); + break; + + case LASTMATCH: + keys = keys.subList(keys.size() - 1, keys.size()); + break; + + default: + // keep keys unmodified + } + } + + try { + if (SyncDeltaType.CREATE_OR_UPDATE == delta.getDeltaType()) { + if (keys.isEmpty()) { + switch (profile.getTask().getUnmatchingRule()) { + case ASSIGN: + profile.getResults().addAll(assign(delta, orgUnit)); + break; + + case PROVISION: + profile.getResults().addAll(provision(delta, orgUnit)); + break; + + case IGNORE: + profile.getResults().add(ignore(delta, false)); + break; + + default: + // do nothing + } + } else { + switch (profile.getTask().getMatchingRule()) { + case UPDATE: + profile.getResults().addAll(update(delta, keys)); + break; + + case DEPROVISION: + profile.getResults().addAll(deprovision(delta, keys, false)); + break; + + case UNASSIGN: + profile.getResults().addAll(deprovision(delta, keys, true)); + break; + + case LINK: + profile.getResults().addAll(link(delta, keys, false)); + break; + + case UNLINK: + profile.getResults().addAll(link(delta, keys, true)); + break; + + case IGNORE: + profile.getResults().add(ignore(delta, true)); + break; + + default: + // do nothing + } + } + } else if (SyncDeltaType.DELETE == delta.getDeltaType()) { + if (keys.isEmpty()) { + finalize(ResourceOperation.DELETE.name().toLowerCase(), Result.SUCCESS, null, null, delta); + LOG.debug("No match found for deletion"); + } else { + profile.getResults().addAll(delete(delta, keys)); + } + } + } catch (IllegalStateException | IllegalArgumentException e) { + LOG.warn(e.getMessage()); + } + } + + private void finalize( + final String event, + final Result result, + final Object before, + final Object output, + final SyncDelta delta) { + + synchronized (this) { + this.latestResult = result; + } + + notificationManager.createTasks(AuditElements.EventCategoryType.PULL, + REALM_TYPE.toLowerCase(), + profile.getTask().getResource().getKey(), + event, + result, + before, + output, + delta); + + auditManager.audit(AuditElements.EventCategoryType.PULL, + REALM_TYPE.toLowerCase(), + profile.getTask().getResource().getKey(), + event, + result, + before, + output, + delta); + } +}