http://git-wip-us.apache.org/repos/asf/syncope/blob/6dfedd8f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractTaskJob.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractTaskJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractTaskJob.java deleted file mode 100644 index 728ab41..0000000 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractTaskJob.java +++ /dev/null @@ -1,216 +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.job; - -import java.util.Date; -import java.util.concurrent.atomic.AtomicReference; -import org.apache.syncope.common.lib.types.AuditElements; -import org.apache.syncope.common.lib.types.AuditElements.Result; -import org.apache.syncope.core.persistence.api.dao.TaskDAO; -import org.apache.syncope.core.persistence.api.dao.TaskExecDAO; -import org.apache.syncope.core.persistence.api.entity.EntityFactory; -import org.apache.syncope.core.persistence.api.entity.task.Task; -import org.apache.syncope.core.persistence.api.entity.task.TaskExec; -import org.apache.syncope.core.provisioning.api.job.TaskJob; -import org.apache.syncope.core.misc.AuditManager; -import org.apache.syncope.core.misc.DataFormat; -import org.apache.syncope.core.misc.ExceptionUtils2; -import org.apache.syncope.core.persistence.api.dao.ConfDAO; -import org.apache.syncope.core.provisioning.api.notification.NotificationManager; -import org.quartz.DisallowConcurrentExecution; -import org.quartz.JobExecutionContext; -import org.quartz.JobExecutionException; -import org.quartz.UnableToInterruptJobException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; - -/** - * Abstract job implementation that delegates to concrete implementation the actual job execution and provides some - * base features. - * <strong>Extending this class will not provide support transaction management.</strong><br/> - * Extend <tt>AbstractTransactionalTaskJob</tt> for this purpose. - * - * @see AbstractTransactionalTaskJob - */ -@DisallowConcurrentExecution -public abstract class AbstractTaskJob implements TaskJob { - - /** - * Task execution status. - */ - public enum Status { - - SUCCESS, - FAILURE - - } - - /** - * Logger. - */ - protected static final Logger LOG = LoggerFactory.getLogger(AbstractTaskJob.class); - - /** - * Task DAO. - */ - @Autowired - protected TaskDAO taskDAO; - - /** - * Task execution DAO. - */ - @Autowired - private TaskExecDAO taskExecDAO; - - /** - * Configuration DAO. - */ - @Autowired - private ConfDAO confDAO; - - /** - * Notification manager. - */ - @Autowired - private NotificationManager notificationManager; - - /** - * Audit manager. - */ - @Autowired - private AuditManager auditManager; - - @Autowired - private EntityFactory entityFactory; - - /** - * Id, set by the caller, for identifying the task to be executed. - */ - protected Long taskId; - - /** - * The actual task to be executed. - */ - protected Task task; - - /** - * The current running thread containing the task to be executed. - */ - protected AtomicReference<Thread> runningThread = new AtomicReference<Thread>(); - - /** - * Task id setter. - * - * @param taskId to be set - */ - @Override - public void setTaskId(final Long taskId) { - this.taskId = taskId; - } - - @Override - public void execute(final JobExecutionContext context) throws JobExecutionException { - this.runningThread.set(Thread.currentThread()); - task = taskDAO.find(taskId); - if (task == null) { - throw new JobExecutionException("Task " + taskId + " not found"); - } - - TaskExec execution = entityFactory.newEntity(TaskExec.class); - execution.setStartDate(new Date()); - execution.setTask(task); - - Result result; - - try { - execution.setMessage(doExecute(context.getMergedJobDataMap().getBoolean(DRY_RUN_JOBDETAIL_KEY))); - execution.setStatus(Status.SUCCESS.name()); - result = Result.SUCCESS; - } catch (JobExecutionException e) { - LOG.error("While executing task " + taskId, e); - result = Result.FAILURE; - - execution.setMessage(ExceptionUtils2.getFullStackTrace(e)); - execution.setStatus(Status.FAILURE.name()); - } - execution.setEndDate(new Date()); - - if (hasToBeRegistered(execution)) { - taskExecDAO.saveAndAdd(taskId, execution); - } - task = taskDAO.save(task); - - notificationManager.createTasks( - AuditElements.EventCategoryType.TASK, - this.getClass().getSimpleName(), - null, - this.getClass().getSimpleName(), // searching for before object is too much expensive ... - result, - task, - execution); - - auditManager.audit( - AuditElements.EventCategoryType.TASK, - task.getClass().getSimpleName(), - null, - null, // searching for before object is too much expensive ... - result, - task, - (Object[]) null); - } - - /** - * The actual execution, delegated to child classes. - * - * @param dryRun whether to actually touch the data - * @return the task execution status to be set - * @throws JobExecutionException if anything goes wrong - */ - protected abstract String doExecute(boolean dryRun) throws JobExecutionException; - - /** - * Template method to determine whether this job's task execution has to be persisted or not. - * - * @param execution task execution - * @return wether to persist or not - */ - protected boolean hasToBeRegistered(final TaskExec execution) { - return false; - } - - @Override - public void interrupt() throws UnableToInterruptJobException { - Thread thread = this.runningThread.getAndSet(null); - if (thread == null) { - LOG.warn("Unable to retrieve the thread of the current job execution"); - } else { - LOG.info("Interrupting job from thread {} at {} ", thread.getId(), DataFormat.format(new Date())); - - long maxRetry = confDAO.find("tasks.interruptMaxRetries", "1").getValues().get(0).getLongValue(); - for (int i = 0; i < maxRetry && thread.isAlive(); i++) { - thread.interrupt(); - } - // if the thread is still alive, it should be available in the next stop - if (thread.isAlive()) { - this.runningThread.set(thread); - } - } - } -}
http://git-wip-us.apache.org/repos/asf/syncope/blob/6dfedd8f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractTransactionalTaskJob.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractTransactionalTaskJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractTransactionalTaskJob.java deleted file mode 100644 index b90c142..0000000 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractTransactionalTaskJob.java +++ /dev/null @@ -1,35 +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.job; - -import org.quartz.JobExecutionContext; -import org.quartz.JobExecutionException; -import org.springframework.transaction.annotation.Transactional; - -/** - * Abstract job implementation for transactional execution. - */ -public abstract class AbstractTransactionalTaskJob extends AbstractTaskJob { - - @Transactional - @Override - public void execute(final JobExecutionContext context) throws JobExecutionException { - super.execute(context); - } -} http://git-wip-us.apache.org/repos/asf/syncope/blob/6dfedd8f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SampleJob.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SampleJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SampleJob.java deleted file mode 100644 index e031905..0000000 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SampleJob.java +++ /dev/null @@ -1,52 +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.job; - -import org.apache.syncope.core.persistence.api.entity.task.SchedTask; -import org.apache.syncope.core.persistence.api.entity.task.TaskExec; -import org.quartz.JobExecutionException; - -/** - * Sample implementation for execution a scheduled task. - * - * @see SchedTask - */ -public class SampleJob extends AbstractTaskJob { - - @Override - protected String doExecute(final boolean dryRun) throws JobExecutionException { - if (!(task instanceof SchedTask)) { - throw new JobExecutionException("Task " + taskId + " isn't a SchedTask"); - } - final SchedTask schedTask = (SchedTask) this.task; - - LOG.info("SampleJob {}running [SchedTask {}]", (dryRun - ? "dry " - : ""), schedTask.getKey()); - - return (dryRun - ? "DRY " - : "") + "RUNNING"; - } - - @Override - protected boolean hasToBeRegistered(final TaskExec execution) { - return true; - } -} http://git-wip-us.apache.org/repos/asf/syncope/blob/6dfedd8f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SpringBeanJobFactory.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SpringBeanJobFactory.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SpringBeanJobFactory.java index fc3938e..925c6e6 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SpringBeanJobFactory.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SpringBeanJobFactory.java @@ -26,6 +26,10 @@ import org.springframework.beans.PropertyAccessorFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; +/** + * An implementation of SpringBeanJobFactory that retrieves the bean from the Spring context so that autowiring and + * transactions work. + */ public class SpringBeanJobFactory extends org.springframework.scheduling.quartz.SpringBeanJobFactory { private String[] ignoredUnknownProperties; @@ -33,7 +37,7 @@ public class SpringBeanJobFactory extends org.springframework.scheduling.quartz. private SchedulerContext schedulerContext; @Override - public void setIgnoredUnknownProperties(final String[] ignoredUnknownProperties) { + public void setIgnoredUnknownProperties(final String... ignoredUnknownProperties) { String[] defensiveCopy = ignoredUnknownProperties.clone(); super.setIgnoredUnknownProperties(defensiveCopy); this.ignoredUnknownProperties = defensiveCopy; @@ -45,12 +49,6 @@ public class SpringBeanJobFactory extends org.springframework.scheduling.quartz. this.schedulerContext = schedulerContext; } - /** - * An implementation of SpringBeanJobFactory that retrieves the bean from the Spring context so that autowiring and - * transactions work. - * - * {@inheritDoc} - */ @Override protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception { final ApplicationContext ctx = ((ConfigurableApplicationContext) schedulerContext.get("applicationContext")); http://git-wip-us.apache.org/repos/asf/syncope/blob/6dfedd8f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java new file mode 100644 index 0000000..2001e22 --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java @@ -0,0 +1,119 @@ +/* + * 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.job; + +import java.util.Date; +import java.util.concurrent.atomic.AtomicReference; +import org.apache.commons.lang3.ClassUtils; +import org.apache.syncope.core.misc.DataFormat; +import org.apache.syncope.core.misc.security.AuthContextUtils; +import org.apache.syncope.core.misc.spring.ApplicationContextProvider; +import org.apache.syncope.core.provisioning.api.job.JobInstanceLoader; +import org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate; +import org.quartz.DisallowConcurrentExecution; +import org.quartz.InterruptableJob; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.quartz.UnableToInterruptJobException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.support.AbstractBeanDefinition; + +@DisallowConcurrentExecution +public class TaskJob implements InterruptableJob { + + private static final Logger LOG = LoggerFactory.getLogger(TaskJob.class); + + public static final String DRY_RUN_JOBDETAIL_KEY = "dryRun"; + + public static final String DELEGATE_CLASS_KEY = "delegateClass"; + + public static final String INTERRUPT_MAX_RETRIES_KEY = "interruptMaxRetries"; + + /** + * Task execution status. + */ + public enum Status { + + SUCCESS, + FAILURE + + } + + /** + * The current running thread containing the task to be executed. + */ + private final AtomicReference<Thread> runningThread = new AtomicReference<>(); + + /** + * Key, set by the caller, for identifying the task to be executed. + */ + private Long taskKey; + + private long interruptMaxRetries = 1; + + /** + * Task key setter. + * + * @param taskKey to be set + */ + public void setTaskKey(final Long taskKey) { + this.taskKey = taskKey; + } + + @Override + public void execute(final JobExecutionContext context) throws JobExecutionException { + this.runningThread.set(Thread.currentThread()); + this.interruptMaxRetries = context.getMergedJobDataMap().getLong(INTERRUPT_MAX_RETRIES_KEY); + + AuthContextUtils.setFakeAuth(context.getMergedJobDataMap().getString(JobInstanceLoader.DOMAIN)); + try { + Class<?> delegateClass = ClassUtils.getClass(context.getMergedJobDataMap().getString(DELEGATE_CLASS_KEY)); + + ((SchedTaskJobDelegate) ApplicationContextProvider.getBeanFactory(). + createBean(delegateClass, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false)). + execute(taskKey, context.getMergedJobDataMap().getBoolean(DRY_RUN_JOBDETAIL_KEY)); + } catch (Exception e) { + throw new JobExecutionException(e); + } finally { + AuthContextUtils.clearFakeAuth(); + } + } + + @Override + public void interrupt() throws UnableToInterruptJobException { + Thread thread = this.runningThread.getAndSet(null); + if (thread == null) { + LOG.warn("Unable to retrieve the thread of the current job execution"); + } else { + LOG.info("Interrupting job from thread {} at {} ", thread.getId(), DataFormat.format(new Date())); + + if (interruptMaxRetries < 1) { + interruptMaxRetries = 1; + } + for (int i = 0; i < interruptMaxRetries && thread.isAlive(); i++) { + thread.interrupt(); + } + // if the thread is still alive, it should be available in the next stop + if (thread.isAlive()) { + this.runningThread.set(thread); + } + } + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/6dfedd8f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java index a73f5b9..1c704df 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java @@ -195,11 +195,11 @@ public class PropagationManagerImpl implements PropagationManager { } @Override - public List<PropagationTask> getUserUpdateTasks(final User user, final Boolean enable, + public List<PropagationTask> getUserUpdateTasks(final Long key, final Boolean enable, final Collection<String> noPropResourceNames) { return getUpdateTasks( - user, // user to be updated on external resources + userDAO.find(key), // user to be updated on external resources null, // no password false, enable, // status to be propagated @@ -236,7 +236,7 @@ public class PropagationManagerImpl implements PropagationManager { } else { // b. generate the propagation task list in two phases: first the ones containing password, // the the rest (with no password) - final PropagationByResource origPropByRes = new PropagationByResource(); + PropagationByResource origPropByRes = new PropagationByResource(); origPropByRes.merge(wfResult.getPropByRes()); Set<String> pwdResourceNames = new HashSet<>(userMod.getPwdPropRequest().getResourceNames()); http://git-wip-us.apache.org/repos/asf/syncope/blob/6dfedd8f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractProvisioningJob.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractProvisioningJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractProvisioningJob.java deleted file mode 100644 index 34ca299..0000000 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractProvisioningJob.java +++ /dev/null @@ -1,480 +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.sync; - -import static org.apache.syncope.common.lib.types.AnyTypeKind.USER; - -import java.lang.reflect.ParameterizedType; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import javax.annotation.Resource; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.collections4.Transformer; -import org.apache.syncope.common.lib.SyncopeConstants; -import org.apache.syncope.common.lib.types.Entitlement; -import org.apache.syncope.common.lib.types.TraceLevel; -import org.apache.syncope.core.misc.security.SyncopeAuthenticationDetails; -import org.apache.syncope.core.misc.security.SyncopeGrantedAuthority; -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.sync.ProvisioningActions; -import org.apache.syncope.core.provisioning.api.sync.ProvisioningResult; -import org.apache.syncope.core.provisioning.java.job.AbstractTaskJob; -import org.quartz.JobExecutionException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.User; - -/** - * Job for executing synchronization tasks. - * - * @see AbstractTaskJob - * @see org.apache.syncope.core.persistence.api.entity.task.SyncTask - * @see org.apache.syncope.core.persistence.api.entity.task.PushTask - */ -public abstract class AbstractProvisioningJob<T extends ProvisioningTask, A extends ProvisioningActions> - extends AbstractTaskJob { - - @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; - - /** - * SyncJob actions. - */ - protected List<A> actions; - - public void setActions(final List<A> actions) { - this.actions = actions; - } - - /** - * 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<ProvisioningResult> 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<ProvisioningResult> uSuccCreate = new ArrayList<>(); - List<ProvisioningResult> uFailCreate = new ArrayList<>(); - List<ProvisioningResult> uSuccUpdate = new ArrayList<>(); - List<ProvisioningResult> uFailUpdate = new ArrayList<>(); - List<ProvisioningResult> uSuccDelete = new ArrayList<>(); - List<ProvisioningResult> uFailDelete = new ArrayList<>(); - List<ProvisioningResult> uSuccNone = new ArrayList<>(); - List<ProvisioningResult> uIgnore = new ArrayList<>(); - List<ProvisioningResult> gSuccCreate = new ArrayList<>(); - List<ProvisioningResult> gFailCreate = new ArrayList<>(); - List<ProvisioningResult> gSuccUpdate = new ArrayList<>(); - List<ProvisioningResult> gFailUpdate = new ArrayList<>(); - List<ProvisioningResult> gSuccDelete = new ArrayList<>(); - List<ProvisioningResult> gFailDelete = new ArrayList<>(); - List<ProvisioningResult> gSuccNone = new ArrayList<>(); - List<ProvisioningResult> gIgnore = new ArrayList<>(); - List<ProvisioningResult> aSuccCreate = new ArrayList<>(); - List<ProvisioningResult> aFailCreate = new ArrayList<>(); - List<ProvisioningResult> aSuccUpdate = new ArrayList<>(); - List<ProvisioningResult> aFailUpdate = new ArrayList<>(); - List<ProvisioningResult> aSuccDelete = new ArrayList<>(); - List<ProvisioningResult> aFailDelete = new ArrayList<>(); - List<ProvisioningResult> aSuccNone = new ArrayList<>(); - List<ProvisioningResult> aIgnore = new ArrayList<>(); - - for (ProvisioningResult 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(ProvisioningResult.produceReport(uFailCreate, syncTraceLevel)); - } - if (!uFailUpdate.isEmpty()) { - report.append("\nUsers failed to update: "); - report.append(ProvisioningResult.produceReport(uFailUpdate, syncTraceLevel)); - } - if (!uFailDelete.isEmpty()) { - report.append("\nUsers failed to delete: "); - report.append(ProvisioningResult.produceReport(uFailDelete, syncTraceLevel)); - } - - if (!gFailCreate.isEmpty()) { - report.append("\n\nGroups failed to create: "); - report.append(ProvisioningResult.produceReport(gFailCreate, syncTraceLevel)); - } - if (!gFailUpdate.isEmpty()) { - report.append("\nGroups failed to update: "); - report.append(ProvisioningResult.produceReport(gFailUpdate, syncTraceLevel)); - } - if (!gFailDelete.isEmpty()) { - report.append("\nGroups failed to delete: "); - report.append(ProvisioningResult.produceReport(gFailDelete, syncTraceLevel)); - } - - if (!aFailCreate.isEmpty()) { - report.append("\nAny objects failed to create: "); - report.append(ProvisioningResult.produceReport(aFailCreate, syncTraceLevel)); - } - if (!aFailUpdate.isEmpty()) { - report.append("\nAny objects failed to update: "); - report.append(ProvisioningResult.produceReport(aFailUpdate, syncTraceLevel)); - } - if (!aFailDelete.isEmpty()) { - report.append("\nAny objects failed to delete: "); - report.append(ProvisioningResult.produceReport(aFailDelete, syncTraceLevel)); - } - } - - // Succeeded, only if on 'ALL' level - if (syncTraceLevel == TraceLevel.ALL) { - report.append("\n\nUsers created:\n"). - append(ProvisioningResult.produceReport(uSuccCreate, syncTraceLevel)). - append("\nUsers updated:\n"). - append(ProvisioningResult.produceReport(uSuccUpdate, syncTraceLevel)). - append("\nUsers deleted:\n"). - append(ProvisioningResult.produceReport(uSuccDelete, syncTraceLevel)). - append("\nUsers no operation:\n"). - append(ProvisioningResult.produceReport(uSuccNone, syncTraceLevel)). - append("\nUsers ignored:\n"). - append(ProvisioningResult.produceReport(uIgnore, syncTraceLevel)); - report.append("\n\nGroups created:\n"). - append(ProvisioningResult.produceReport(gSuccCreate, syncTraceLevel)). - append("\nGroups updated:\n"). - append(ProvisioningResult.produceReport(gSuccUpdate, syncTraceLevel)). - append("\nGroups deleted:\n"). - append(ProvisioningResult.produceReport(gSuccDelete, syncTraceLevel)). - append("\nGroups no operation:\n"). - append(ProvisioningResult.produceReport(gSuccNone, syncTraceLevel)). - append("\nGroups ignored:\n"). - append(ProvisioningResult.produceReport(gSuccNone, syncTraceLevel)); - report.append("\n\nAny objects created:\n"). - append(ProvisioningResult.produceReport(aSuccCreate, syncTraceLevel)). - append("\nAny objects updated:\n"). - append(ProvisioningResult.produceReport(aSuccUpdate, syncTraceLevel)). - append("\nAny objects deleted:\n"). - append(ProvisioningResult.produceReport(aSuccDelete, syncTraceLevel)). - append("\nAny objects no operation:\n"). - append(ProvisioningResult.produceReport(aSuccNone, syncTraceLevel)). - append("\nAny objects ignored:\n"). - append(ProvisioningResult.produceReport(aSuccNone, syncTraceLevel)); - } - - return report.toString(); - } - - @Override - protected String doExecute(final boolean dryRun) throws JobExecutionException { - // PRE: grant all authorities (i.e. setup the SecurityContextHolder) - List<GrantedAuthority> authorities = CollectionUtils.collect(Entitlement.values(), - new Transformer<String, GrantedAuthority>() { - - @Override - public GrantedAuthority transform(final String entitlement) { - return new SyncopeGrantedAuthority(entitlement, SyncopeConstants.ROOT_REALM); - } - }, new ArrayList<GrantedAuthority>()); - - UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken( - new User(adminUser, "FAKE_PASSWORD", authorities), "FAKE_PASSWORD", authorities); - auth.setDetails(new SyncopeAuthenticationDetails(taskDAO.getDomain(task))); - SecurityContextHolder.getContext().setAuthentication(auth); - - try { - Class<T> clazz = getTaskClassReference(); - if (!clazz.isAssignableFrom(task.getClass())) { - throw new JobExecutionException("Task " + taskId + " isn't a SyncTask"); - } - - 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 executeWithSecurityContext( - provisioningTask, - connector, - dryRun); - } catch (Throwable t) { - LOG.error("While executing provisioning job {}", getClass().getName(), t); - throw t; - } finally { - // POST: clean up the SecurityContextHolder - SecurityContextHolder.clearContext(); - } - } - - protected abstract String executeWithSecurityContext( - 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 (Status.valueOf(execution.getStatus()) == 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/6dfedd8f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractProvisioningJobDelegate.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractProvisioningJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractProvisioningJobDelegate.java new file mode 100644 index 0000000..0c8deb2 --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/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.sync; + +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.sync.ProvisioningResult; +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<ProvisioningResult> 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<ProvisioningResult> uSuccCreate = new ArrayList<>(); + List<ProvisioningResult> uFailCreate = new ArrayList<>(); + List<ProvisioningResult> uSuccUpdate = new ArrayList<>(); + List<ProvisioningResult> uFailUpdate = new ArrayList<>(); + List<ProvisioningResult> uSuccDelete = new ArrayList<>(); + List<ProvisioningResult> uFailDelete = new ArrayList<>(); + List<ProvisioningResult> uSuccNone = new ArrayList<>(); + List<ProvisioningResult> uIgnore = new ArrayList<>(); + List<ProvisioningResult> gSuccCreate = new ArrayList<>(); + List<ProvisioningResult> gFailCreate = new ArrayList<>(); + List<ProvisioningResult> gSuccUpdate = new ArrayList<>(); + List<ProvisioningResult> gFailUpdate = new ArrayList<>(); + List<ProvisioningResult> gSuccDelete = new ArrayList<>(); + List<ProvisioningResult> gFailDelete = new ArrayList<>(); + List<ProvisioningResult> gSuccNone = new ArrayList<>(); + List<ProvisioningResult> gIgnore = new ArrayList<>(); + List<ProvisioningResult> aSuccCreate = new ArrayList<>(); + List<ProvisioningResult> aFailCreate = new ArrayList<>(); + List<ProvisioningResult> aSuccUpdate = new ArrayList<>(); + List<ProvisioningResult> aFailUpdate = new ArrayList<>(); + List<ProvisioningResult> aSuccDelete = new ArrayList<>(); + List<ProvisioningResult> aFailDelete = new ArrayList<>(); + List<ProvisioningResult> aSuccNone = new ArrayList<>(); + List<ProvisioningResult> aIgnore = new ArrayList<>(); + + for (ProvisioningResult 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(ProvisioningResult.produceReport(uFailCreate, syncTraceLevel)); + } + if (!uFailUpdate.isEmpty()) { + report.append("\nUsers failed to update: "); + report.append(ProvisioningResult.produceReport(uFailUpdate, syncTraceLevel)); + } + if (!uFailDelete.isEmpty()) { + report.append("\nUsers failed to delete: "); + report.append(ProvisioningResult.produceReport(uFailDelete, syncTraceLevel)); + } + + if (!gFailCreate.isEmpty()) { + report.append("\n\nGroups failed to create: "); + report.append(ProvisioningResult.produceReport(gFailCreate, syncTraceLevel)); + } + if (!gFailUpdate.isEmpty()) { + report.append("\nGroups failed to update: "); + report.append(ProvisioningResult.produceReport(gFailUpdate, syncTraceLevel)); + } + if (!gFailDelete.isEmpty()) { + report.append("\nGroups failed to delete: "); + report.append(ProvisioningResult.produceReport(gFailDelete, syncTraceLevel)); + } + + if (!aFailCreate.isEmpty()) { + report.append("\nAny objects failed to create: "); + report.append(ProvisioningResult.produceReport(aFailCreate, syncTraceLevel)); + } + if (!aFailUpdate.isEmpty()) { + report.append("\nAny objects failed to update: "); + report.append(ProvisioningResult.produceReport(aFailUpdate, syncTraceLevel)); + } + if (!aFailDelete.isEmpty()) { + report.append("\nAny objects failed to delete: "); + report.append(ProvisioningResult.produceReport(aFailDelete, syncTraceLevel)); + } + } + + // Succeeded, only if on 'ALL' level + if (syncTraceLevel == TraceLevel.ALL) { + report.append("\n\nUsers created:\n"). + append(ProvisioningResult.produceReport(uSuccCreate, syncTraceLevel)). + append("\nUsers updated:\n"). + append(ProvisioningResult.produceReport(uSuccUpdate, syncTraceLevel)). + append("\nUsers deleted:\n"). + append(ProvisioningResult.produceReport(uSuccDelete, syncTraceLevel)). + append("\nUsers no operation:\n"). + append(ProvisioningResult.produceReport(uSuccNone, syncTraceLevel)). + append("\nUsers ignored:\n"). + append(ProvisioningResult.produceReport(uIgnore, syncTraceLevel)); + report.append("\n\nGroups created:\n"). + append(ProvisioningResult.produceReport(gSuccCreate, syncTraceLevel)). + append("\nGroups updated:\n"). + append(ProvisioningResult.produceReport(gSuccUpdate, syncTraceLevel)). + append("\nGroups deleted:\n"). + append(ProvisioningResult.produceReport(gSuccDelete, syncTraceLevel)). + append("\nGroups no operation:\n"). + append(ProvisioningResult.produceReport(gSuccNone, syncTraceLevel)). + append("\nGroups ignored:\n"). + append(ProvisioningResult.produceReport(gSuccNone, syncTraceLevel)); + report.append("\n\nAny objects created:\n"). + append(ProvisioningResult.produceReport(aSuccCreate, syncTraceLevel)). + append("\nAny objects updated:\n"). + append(ProvisioningResult.produceReport(aSuccUpdate, syncTraceLevel)). + append("\nAny objects deleted:\n"). + append(ProvisioningResult.produceReport(aSuccDelete, syncTraceLevel)). + append("\nAny objects no operation:\n"). + append(ProvisioningResult.produceReport(aSuccNone, syncTraceLevel)). + append("\nAny objects ignored:\n"). + append(ProvisioningResult.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/6dfedd8f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractPushResultHandler.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractPushResultHandler.java index fd5a482..6be2167 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractPushResultHandler.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractPushResultHandler.java @@ -50,6 +50,7 @@ import org.apache.syncope.core.provisioning.api.sync.SyncopePushResultHandler; import org.identityconnectors.framework.common.objects.ConnectorObject; import org.identityconnectors.framework.common.objects.ObjectClass; import org.quartz.JobExecutionException; +import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHandler<PushTask, PushActions> @@ -71,7 +72,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan protected abstract ConnectorObject getRemoteObject(String connObjectKey, ObjectClass objectClass); - @Transactional + @Transactional(propagation = Propagation.REQUIRES_NEW) @Override public boolean handle(final long anyKey) { Any<?, ?, ?> any = null; http://git-wip-us.apache.org/repos/asf/syncope/blob/6dfedd8f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractSyncResultHandler.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractSyncResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractSyncResultHandler.java index 6d8986a..b0b5a58 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractSyncResultHandler.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractSyncResultHandler.java @@ -44,7 +44,9 @@ 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 { @@ -614,9 +616,7 @@ public abstract class AbstractSyncResultHandler extends AbstractSyncopeResultHan * @param provision provisioning info * @throws JobExecutionException in case of synchronization failure. */ - protected void doHandle(final SyncDelta delta, final Provision provision) - throws JobExecutionException { - + protected void doHandle(final SyncDelta delta, final Provision provision) throws JobExecutionException { AnyUtils anyUtils = getAnyUtils(); LOG.debug("Process {} for {} as {}", http://git-wip-us.apache.org/repos/asf/syncope/blob/6dfedd8f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractSyncopeResultHandler.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractSyncopeResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractSyncopeResultHandler.java index cf08d8d..49a38d3 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractSyncopeResultHandler.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractSyncopeResultHandler.java @@ -28,7 +28,6 @@ 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.sync.ProvisioningActions; import org.apache.syncope.core.provisioning.api.sync.ProvisioningProfile; import org.apache.syncope.core.provisioning.api.sync.SyncopeResultHandler; import org.apache.syncope.core.misc.AuditManager; @@ -39,6 +38,7 @@ import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager; 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.sync.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; http://git-wip-us.apache.org/repos/asf/syncope/blob/6dfedd8f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/PushJobDelegate.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/PushJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/PushJobDelegate.java new file mode 100644 index 0000000..ff6ff21 --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/PushJobDelegate.java @@ -0,0 +1,192 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.provisioning.java.sync; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.misc.search.SearchCondConverter; +import org.apache.syncope.core.misc.spring.ApplicationContextProvider; +import org.apache.syncope.core.persistence.api.dao.AnyDAO; +import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; +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.dao.search.OrderByClause; +import org.apache.syncope.core.persistence.api.entity.Any; +import org.apache.syncope.core.persistence.api.entity.resource.Provision; +import org.apache.syncope.core.persistence.api.entity.task.PushTask; +import org.apache.syncope.core.provisioning.api.Connector; +import org.apache.syncope.core.provisioning.api.sync.AnyObjectPushResultHandler; +import org.apache.syncope.core.provisioning.api.sync.GroupPushResultHandler; +import org.apache.syncope.core.provisioning.api.sync.ProvisioningProfile; +import org.apache.syncope.core.provisioning.api.sync.PushActions; +import org.apache.syncope.core.provisioning.api.sync.SyncopePushResultHandler; +import org.apache.syncope.core.provisioning.api.sync.UserPushResultHandler; +import org.quartz.JobExecutionException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.support.AbstractBeanDefinition; + +public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> { + + private static final int PAGE_SIZE = 1000; + + /** + * User DAO. + */ + @Autowired + private UserDAO userDAO; + + /** + * Search DAO. + */ + @Autowired + private AnySearchDAO searchDAO; + + /** + * Group DAO. + */ + @Autowired + private GroupDAO groupDAO; + + @Autowired + private AnyObjectDAO anyObjectDAO; + + private AnyDAO<?> getAnyDAO(final AnyTypeKind anyTypeKind) { + AnyDAO<?> result; + switch (anyTypeKind) { + case USER: + result = userDAO; + break; + + case GROUP: + result = groupDAO; + break; + + case ANY_OBJECT: + default: + result = anyObjectDAO; + } + + return result; + } + + @Override + protected String doExecuteProvisioning( + final PushTask pushTask, + final Connector connector, + final boolean dryRun) throws JobExecutionException { + + LOG.debug("Executing push on {}", pushTask.getResource()); + + List<PushActions> actions = new ArrayList<>(); + for (String className : pushTask.getActionsClassNames()) { + try { + Class<?> actionsClass = Class.forName(className); + + PushActions syncActions = (PushActions) ApplicationContextProvider.getBeanFactory(). + createBean(actionsClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, true); + actions.add(syncActions); + } catch (Exception e) { + LOG.info("Class '{}' not found", className, e); + } + } + + ProvisioningProfile<PushTask, PushActions> profile = new ProvisioningProfile<>(connector, pushTask); + profile.setDryRun(dryRun); + profile.setResAct(null); + + AnyObjectPushResultHandler ahandler = + (AnyObjectPushResultHandler) ApplicationContextProvider.getBeanFactory(). + createBean(AnyObjectPushResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false); + ahandler.setProfile(profile); + + UserPushResultHandler uhandler = + (UserPushResultHandler) ApplicationContextProvider.getBeanFactory(). + createBean(UserPushResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false); + uhandler.setProfile(profile); + + GroupPushResultHandler ghandler = + (GroupPushResultHandler) ApplicationContextProvider.getBeanFactory(). + createBean(GroupPushResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false); + ghandler.setProfile(profile); + + if (!profile.isDryRun()) { + for (PushActions action : actions) { + action.beforeAll(profile); + } + } + + for (Provision provision : pushTask.getResource().getProvisions()) { + if (provision.getMapping() != null) { + AnyDAO<?> anyDAO = getAnyDAO(provision.getAnyType().getKind()); + String filter = pushTask.getFilter(provision.getAnyType()) == null + ? null + : pushTask.getFilter(provision.getAnyType()).get(); + + int count = anyDAO.count(SyncopeConstants.FULL_ADMIN_REALMS); + for (int page = 1; page <= (count / PAGE_SIZE) + 1; page++) { + List<? extends Any<?, ?, ?>> localAnys = StringUtils.isBlank(filter) + ? anyDAO.findAll(SyncopeConstants.FULL_ADMIN_REALMS, page, PAGE_SIZE) + : searchDAO.search(SyncopeConstants.FULL_ADMIN_REALMS, + SearchCondConverter.convert(filter), + Collections.<OrderByClause>emptyList(), provision.getAnyType().getKind()); + + for (Any<?, ?, ?> any : localAnys) { + SyncopePushResultHandler handler; + switch (provision.getAnyType().getKind()) { + case USER: + handler = uhandler; + break; + + case GROUP: + handler = ghandler; + break; + + case ANY_OBJECT: + default: + handler = ahandler; + } + + try { + handler.handle(any.getKey()); + } catch (Exception e) { + LOG.warn("Failure pushing '{}' on '{}'", any, pushTask.getResource(), e); + throw new JobExecutionException( + "While pushing " + any + " on " + pushTask.getResource(), e); + } + } + } + } + } + + if (!profile.isDryRun()) { + for (PushActions action : actions) { + action.afterAll(profile); + } + } + + String result = createReport(profile.getResults(), pushTask.getResource().getSyncTraceLevel(), dryRun); + LOG.debug("Sync result: {}", result); + return result; + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/6dfedd8f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/PushJobImpl.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/PushJobImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/PushJobImpl.java deleted file mode 100644 index b658bc4..0000000 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/PushJobImpl.java +++ /dev/null @@ -1,189 +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.sync; - -import java.util.Collections; -import java.util.List; -import org.apache.commons.lang3.StringUtils; -import org.apache.syncope.common.lib.SyncopeConstants; -import org.apache.syncope.common.lib.types.AnyTypeKind; -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.dao.search.OrderByClause; -import org.apache.syncope.core.persistence.api.entity.task.PushTask; -import org.apache.syncope.core.provisioning.api.Connector; -import org.apache.syncope.core.provisioning.api.sync.ProvisioningProfile; -import org.apache.syncope.core.provisioning.api.sync.PushActions; -import org.apache.syncope.core.misc.spring.ApplicationContextProvider; -import org.apache.syncope.core.misc.search.SearchCondConverter; -import org.apache.syncope.core.persistence.api.dao.AnyDAO; -import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; -import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; -import org.apache.syncope.core.persistence.api.entity.Any; -import org.apache.syncope.core.persistence.api.entity.resource.Provision; -import org.apache.syncope.core.provisioning.api.job.PushJob; -import org.apache.syncope.core.provisioning.api.sync.AnyObjectPushResultHandler; -import org.apache.syncope.core.provisioning.api.sync.GroupPushResultHandler; -import org.apache.syncope.core.provisioning.api.sync.SyncopePushResultHandler; -import org.apache.syncope.core.provisioning.api.sync.UserPushResultHandler; -import org.quartz.JobExecutionException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.support.AbstractBeanDefinition; - -/** - * Job for executing synchronization (towards external resource) tasks. - * - * @see AbstractProvisioningJob - * @see PushTask - * @see PushActions - */ -public class PushJobImpl extends AbstractProvisioningJob<PushTask, PushActions> implements PushJob { - - private static final int PAGE_SIZE = 1000; - - /** - * User DAO. - */ - @Autowired - private UserDAO userDAO; - - /** - * Search DAO. - */ - @Autowired - private AnySearchDAO searchDAO; - - /** - * Group DAO. - */ - @Autowired - private GroupDAO groupDAO; - - @Autowired - private AnyObjectDAO anyObjectDAO; - - private AnyDAO<?> getAnyDAO(final AnyTypeKind anyTypeKind) { - AnyDAO<?> result; - switch (anyTypeKind) { - case USER: - result = userDAO; - break; - - case GROUP: - result = groupDAO; - break; - - case ANY_OBJECT: - default: - result = anyObjectDAO; - } - - return result; - } - - @Override - protected String executeWithSecurityContext( - final PushTask pushTask, - final Connector connector, - final boolean dryRun) throws JobExecutionException { - - LOG.debug("Executing push on {}", pushTask.getResource()); - - ProvisioningProfile<PushTask, PushActions> profile = new ProvisioningProfile<>(connector, pushTask); - if (actions != null) { - profile.getActions().addAll(actions); - } - profile.setDryRun(dryRun); - profile.setResAct(null); - - AnyObjectPushResultHandler ahandler = - (AnyObjectPushResultHandler) ApplicationContextProvider.getApplicationContext().getBeanFactory(). - createBean(AnyObjectPushResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false); - ahandler.setProfile(profile); - - UserPushResultHandler uhandler = - (UserPushResultHandler) ApplicationContextProvider.getApplicationContext().getBeanFactory(). - createBean(UserPushResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false); - uhandler.setProfile(profile); - - GroupPushResultHandler ghandler = - (GroupPushResultHandler) ApplicationContextProvider.getApplicationContext().getBeanFactory(). - createBean(GroupPushResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false); - ghandler.setProfile(profile); - - if (actions != null && !profile.isDryRun()) { - for (PushActions action : actions) { - action.beforeAll(profile); - } - } - - for (Provision provision : pushTask.getResource().getProvisions()) { - if (provision.getMapping() != null) { - AnyDAO<?> anyDAO = getAnyDAO(provision.getAnyType().getKind()); - String filter = pushTask.getFilter(provision.getAnyType()) == null - ? null - : pushTask.getFilter(provision.getAnyType()).get(); - - int count = anyDAO.count(SyncopeConstants.FULL_ADMIN_REALMS); - for (int page = 1; page <= (count / PAGE_SIZE) + 1; page++) { - List<? extends Any<?, ?, ?>> localAnys = StringUtils.isBlank(filter) - ? anyDAO.findAll(SyncopeConstants.FULL_ADMIN_REALMS, page, PAGE_SIZE) - : searchDAO.search(SyncopeConstants.FULL_ADMIN_REALMS, - SearchCondConverter.convert(filter), - Collections.<OrderByClause>emptyList(), provision.getAnyType().getKind()); - - for (Any<?, ?, ?> any : localAnys) { - SyncopePushResultHandler handler; - switch (provision.getAnyType().getKind()) { - case USER: - handler = uhandler; - break; - - case GROUP: - handler = ghandler; - break; - - case ANY_OBJECT: - default: - handler = ahandler; - } - - try { - handler.handle(any.getKey()); - } catch (Exception e) { - LOG.warn("Failure pushing '{}' on '{}'", any, pushTask.getResource(), e); - throw new JobExecutionException( - "While pushing " + any + " on " + pushTask.getResource(), e); - } - } - } - } - } - - if (actions != null && !profile.isDryRun()) { - for (PushActions action : actions) { - action.afterAll(profile); - } - } - - String result = createReport(profile.getResults(), pushTask.getResource().getSyncTraceLevel(), dryRun); - LOG.debug("Sync result: {}", result); - return result; - } -}
