Repository: syncope Updated Branches: refs/heads/1_2_X b68778e34 -> b43c9c86d
[SYNCOPE-660] Asynchronous jobs delegated to Quartz, like Task and Report, now can be checked and interrupted during execution Project: http://git-wip-us.apache.org/repos/asf/syncope/repo Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/358aef72 Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/358aef72 Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/358aef72 Branch: refs/heads/1_2_X Commit: 358aef72dca5672361cf1a9a3ac2b8c0c57517a3 Parents: 1d6451b Author: giacomolm <[email protected]> Authored: Tue Apr 14 16:33:45 2015 +0200 Committer: giacomolm <[email protected]> Committed: Thu May 21 17:00:47 2015 +0200 ---------------------------------------------------------------------- .../syncope/common/services/ReportService.java | 24 +++ .../syncope/common/services/TaskService.java | 23 +++ .../apache/syncope/common/types/JobAction.java | 29 ++++ .../syncope/common/types/JobStatusType.java | 30 ++++ core/pom.xml | 1 + .../syncope/core/quartz/AbstractTaskJob.java | 25 ++- .../org/apache/syncope/core/quartz/TaskJob.java | 4 +- .../rest/controller/AbstractJobController.java | 155 +++++++++++++++++++ .../core/rest/controller/ReportController.java | 30 +++- .../core/rest/controller/TaskController.java | 26 +++- .../core/services/ReportServiceImpl.java | 42 +++-- .../syncope/core/services/TaskServiceImpl.java | 12 ++ .../syncope/core/quartz/TestSampleJob.java | 63 ++++++++ .../syncope/core/rest/TaskTestITCase.java | 60 +++++++ 14 files changed, 500 insertions(+), 24 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/syncope/blob/358aef72/common/src/main/java/org/apache/syncope/common/services/ReportService.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/org/apache/syncope/common/services/ReportService.java b/common/src/main/java/org/apache/syncope/common/services/ReportService.java index 4bf9c27..47a75ac 100644 --- a/common/src/main/java/org/apache/syncope/common/services/ReportService.java +++ b/common/src/main/java/org/apache/syncope/common/services/ReportService.java @@ -25,6 +25,7 @@ import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; +import javax.ws.rs.MatrixParam; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; @@ -39,6 +40,8 @@ import org.apache.cxf.jaxrs.model.wadl.DocTarget; import org.apache.syncope.common.reqres.PagedResult; import org.apache.syncope.common.to.ReportExecTO; import org.apache.syncope.common.to.ReportTO; +import org.apache.syncope.common.types.JobAction; +import org.apache.syncope.common.types.JobStatusType; import org.apache.syncope.common.types.ReportExecExportFormat; import org.apache.syncope.common.wrap.ReportletConfClass; @@ -192,4 +195,25 @@ public interface ReportService extends JAXRSService { @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) Response exportExecutionResult(@NotNull @PathParam("executionId") Long executionId, @QueryParam("format") ReportExecExportFormat fmt); + + /** + * List report jobs of the given type + * + * @param type of report job + * @return List of ReportExecTO + */ + @GET + @Path("jobs") + @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + List<ReportExecTO> list(@MatrixParam("type") JobStatusType type); + + /** + * Execute a control action on an existing report + * + * @param action + * @param reportId id of report + */ + @POST + @Path("{reportId}") + void process(@QueryParam("action") JobAction action, @PathParam("reportId") Long reportId); } http://git-wip-us.apache.org/repos/asf/syncope/blob/358aef72/common/src/main/java/org/apache/syncope/common/services/TaskService.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/org/apache/syncope/common/services/TaskService.java b/common/src/main/java/org/apache/syncope/common/services/TaskService.java index 8efba34..70929db 100644 --- a/common/src/main/java/org/apache/syncope/common/services/TaskService.java +++ b/common/src/main/java/org/apache/syncope/common/services/TaskService.java @@ -44,6 +44,8 @@ import org.apache.syncope.common.to.ReportExecTO; import org.apache.syncope.common.to.TaskExecTO; import org.apache.syncope.common.to.AbstractTaskTO; import org.apache.syncope.common.to.SchedTaskTO; +import org.apache.syncope.common.types.JobAction; +import org.apache.syncope.common.types.JobStatusType; import org.apache.syncope.common.types.TaskType; import org.apache.syncope.common.wrap.JobClass; import org.apache.syncope.common.wrap.PushActionClass; @@ -242,4 +244,25 @@ public interface TaskService extends JAXRSService { @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) BulkActionResult bulk(@NotNull BulkAction bulkAction); + + /** + * List task jobs of the given type + * + * @param type of task job + * @return List of TaskExecTO + */ + @GET + @Path("jobs") + @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + List<TaskExecTO> list(@MatrixParam("type") JobStatusType type); + + /** + * Execute a control action on an existing task + * + * @param action + * @param taskId id of task + */ + @POST + @Path("{taskId}") + void process(@QueryParam("action") JobAction action, @PathParam("taskId") Long taskId); } http://git-wip-us.apache.org/repos/asf/syncope/blob/358aef72/common/src/main/java/org/apache/syncope/common/types/JobAction.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/org/apache/syncope/common/types/JobAction.java b/common/src/main/java/org/apache/syncope/common/types/JobAction.java new file mode 100644 index 0000000..cbabd2a --- /dev/null +++ b/common/src/main/java/org/apache/syncope/common/types/JobAction.java @@ -0,0 +1,29 @@ +/* + * 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.common.types; + +import javax.xml.bind.annotation.XmlEnum; + +@XmlEnum +public enum JobAction { + + START, + STOP; + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/358aef72/common/src/main/java/org/apache/syncope/common/types/JobStatusType.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/org/apache/syncope/common/types/JobStatusType.java b/common/src/main/java/org/apache/syncope/common/types/JobStatusType.java new file mode 100644 index 0000000..ed5f914 --- /dev/null +++ b/common/src/main/java/org/apache/syncope/common/types/JobStatusType.java @@ -0,0 +1,30 @@ +/* + * 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.common.types; + +import javax.xml.bind.annotation.XmlEnum; + +@XmlEnum +public enum JobStatusType { + + ALL, + RUNNING, + SCHEDULED; + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/358aef72/core/pom.xml ---------------------------------------------------------------------- diff --git a/core/pom.xml b/core/pom.xml index b613270..79c2cfe 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -466,6 +466,7 @@ under the License. <copy file="${project.build.directory}/test-classes/org/apache/syncope/core/sync/TestSyncActions.class" todir="${cargo.run.dir}/WEB-INF/classes/org/apache/syncope/core/sync" /> <copy file="${project.build.directory}/test-classes/org/apache/syncope/core/sync/TestSyncRule.class" todir="${cargo.run.dir}/WEB-INF/classes/org/apache/syncope/core/sync" /> <copy file="${project.build.directory}/test-classes/org/apache/syncope/core/rest/data/DoubleValueAttributableTransformer.class" todir="${cargo.run.dir}/WEB-INF/classes/org/apache/syncope/core/rest/data" /> + <copy file="${project.build.directory}/test-classes/org/apache/syncope/core/quartz/TestSampleJob.class" todir="${cargo.run.dir}/WEB-INF/classes/org/apache/syncope/core/quartz" /> <copy file="${project.build.directory}/test-classes/db.jsp" todir="${cargo.run.dir}" /> </target> </configuration> http://git-wip-us.apache.org/repos/asf/syncope/blob/358aef72/core/src/main/java/org/apache/syncope/core/quartz/AbstractTaskJob.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/syncope/core/quartz/AbstractTaskJob.java b/core/src/main/java/org/apache/syncope/core/quartz/AbstractTaskJob.java index d0b590c..9538311 100644 --- a/core/src/main/java/org/apache/syncope/core/quartz/AbstractTaskJob.java +++ b/core/src/main/java/org/apache/syncope/core/quartz/AbstractTaskJob.java @@ -18,12 +18,15 @@ */ package org.apache.syncope.core.quartz; +import java.text.SimpleDateFormat; import java.util.Date; +import java.util.Locale; +import java.util.concurrent.atomic.AtomicReference; +import org.apache.syncope.common.SyncopeConstants; import org.apache.syncope.common.types.AuditElements; import org.apache.syncope.common.types.AuditElements.Result; import org.apache.syncope.core.audit.AuditManager; import org.apache.syncope.core.notification.NotificationManager; - import org.apache.syncope.core.persistence.beans.Task; import org.apache.syncope.core.persistence.beans.TaskExec; import org.apache.syncope.core.persistence.dao.TaskDAO; @@ -32,6 +35,7 @@ import org.apache.syncope.core.util.ExceptionUtil; 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; @@ -99,6 +103,11 @@ public abstract class AbstractTaskJob implements TaskJob { 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 @@ -110,6 +119,7 @@ public abstract class AbstractTaskJob implements TaskJob { @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"); @@ -176,4 +186,17 @@ public abstract class AbstractTaskJob implements TaskJob { protected boolean hasToBeRegistered(final TaskExec execution) { return false; } + + @Override + public void interrupt() throws UnableToInterruptJobException { + Thread thread = this.runningThread.getAndSet(null); + if (thread != null) { + LOG.info("Interrupting job time {} ", (new SimpleDateFormat(SyncopeConstants.DEFAULT_DATE_PATTERN, Locale. + getDefault())).format(new Date())); + thread.interrupt(); + } else { + LOG.warn("Unable to retrieve the right thread related to the current job execution"); + } + } +; } http://git-wip-us.apache.org/repos/asf/syncope/blob/358aef72/core/src/main/java/org/apache/syncope/core/quartz/TaskJob.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/syncope/core/quartz/TaskJob.java b/core/src/main/java/org/apache/syncope/core/quartz/TaskJob.java index 7423690..d1dd83e 100644 --- a/core/src/main/java/org/apache/syncope/core/quartz/TaskJob.java +++ b/core/src/main/java/org/apache/syncope/core/quartz/TaskJob.java @@ -18,12 +18,12 @@ */ package org.apache.syncope.core.quartz; -import org.quartz.Job; +import org.quartz.InterruptableJob; /** * Interface for Quartz jobs bound to a given Task. */ -public interface TaskJob extends Job { +public interface TaskJob extends InterruptableJob { void setTaskId(Long taskId); } http://git-wip-us.apache.org/repos/asf/syncope/blob/358aef72/core/src/main/java/org/apache/syncope/core/rest/controller/AbstractJobController.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/syncope/core/rest/controller/AbstractJobController.java b/core/src/main/java/org/apache/syncope/core/rest/controller/AbstractJobController.java new file mode 100644 index 0000000..4726330 --- /dev/null +++ b/core/src/main/java/org/apache/syncope/core/rest/controller/AbstractJobController.java @@ -0,0 +1,155 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.rest.controller; + +import static org.apache.syncope.core.rest.controller.AbstractController.LOG; + +import java.util.ArrayList; +import java.util.List; +import org.apache.syncope.common.AbstractBaseBean; +import org.apache.syncope.common.to.AbstractExecTO; +import org.apache.syncope.common.types.JobAction; +import org.apache.syncope.common.types.JobStatusType; +import org.quartz.JobExecutionContext; +import org.quartz.JobKey; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.Trigger; +import org.quartz.impl.matchers.GroupMatcher; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; + +abstract class AbstractJobController<T extends AbstractBaseBean> extends AbstractTransactionalController<T> { + + @Autowired + protected SchedulerFactoryBean scheduler; + + protected abstract Long getIdFromJobName(JobKey jobKey); + + public <E extends AbstractExecTO> List<E> list(final JobStatusType type, final Class<E> reference) { + List<E> jobExecTOs = new ArrayList<E>(); + + switch (type) { + case ALL: + try { + for (String groupName : scheduler.getScheduler().getJobGroupNames()) { + for (JobKey jobKey : scheduler.getScheduler().getJobKeys(GroupMatcher.jobGroupEquals(groupName))) { + + Long jobId = getIdFromJobName(jobKey); + if (jobId != null) { + List<? extends Trigger> jobTriggers = scheduler.getScheduler().getTriggersOfJob(jobKey); + if (jobTriggers.size() > 0) { + for (Trigger t : jobTriggers) { + E jobExecTO = reference.newInstance(); + jobExecTO.setId(jobId); + jobExecTO. + setStatus(scheduler.getScheduler().getTriggerState(t.getKey()).name()); + jobExecTO.setStartDate(t.getStartTime()); + jobExecTOs.add(jobExecTO); + } + } else { + E jobExecTO = reference.newInstance(); + jobExecTO.setId(jobId); + jobExecTO.setStatus("Not Scheduled"); + jobExecTOs.add(jobExecTO); + } + } + } + } + } catch (SchedulerException ex) { + LOG.debug("Problems during retrieving all scheduled jobs {}", ex); + } catch (InstantiationException ex) { + LOG.debug("Problems during instantiating {} {}", reference, ex); + } catch (IllegalAccessException ex) { + LOG.debug("Problems during accessing {} {}", reference, ex); + } + break; + case RUNNING: + try { + for (JobExecutionContext jec : scheduler.getScheduler().getCurrentlyExecutingJobs()) { + Long jobId = getIdFromJobName(jec.getJobDetail().getKey()); + if (jobId != null) { + E jobExecTO = reference.newInstance(); + jobExecTO.setId(jobId); + jobExecTO.setStatus(scheduler.getScheduler().getTriggerState(jec.getTrigger().getKey()). + name()); + jobExecTO.setStartDate(jec.getFireTime()); + jobExecTOs.add(jobExecTO); + } + } + } catch (SchedulerException ex) { + LOG.debug("Problems during retrieving all currently executing jobs {}", ex); + } catch (InstantiationException ex) { + LOG.debug("Problems during instantiating {} {}", reference, ex); + } catch (IllegalAccessException ex) { + LOG.debug("Problems during accessing {} {}", reference, ex); + } + break; + case SCHEDULED: + try { + for (String groupName : scheduler.getScheduler().getJobGroupNames()) { + for (JobKey jobKey : scheduler.getScheduler().getJobKeys(GroupMatcher.jobGroupEquals(groupName))) { + Long jobId = getIdFromJobName(jobKey); + if (jobId != null) { + List<? extends Trigger> jobTriggers = scheduler.getScheduler().getTriggersOfJob(jobKey); + for (Trigger t : jobTriggers) { + E jobExecTO = reference.newInstance(); + jobExecTO.setId(jobId); + jobExecTO.setStatus(scheduler.getScheduler().getTriggerState(t.getKey()).name()); + jobExecTO.setStartDate(t.getStartTime()); + jobExecTOs.add(jobExecTO); + } + } + } + } + } catch (SchedulerException ex) { + LOG.debug("Problems during retrieving all scheduled jobs {}", ex); + } catch (InstantiationException ex) { + LOG.debug("Problems during instantiating {} {}", reference, ex); + } catch (IllegalAccessException ex) { + LOG.debug("Problems during accessing {} {}", reference, ex); + } + break; + default: + } + return jobExecTOs; + } + + protected void process(JobAction action, String jobName) { + + if (jobName != null) { + JobKey jobKey = new JobKey(jobName, Scheduler.DEFAULT_GROUP); + try { + if (scheduler.getScheduler().checkExists(jobKey)) { + switch (action) { + case START: + scheduler.getScheduler().triggerJob(jobKey); + break; + case STOP: + scheduler.getScheduler().interrupt(jobKey); + break; + default: + } + } + } catch (SchedulerException ex) { + LOG.debug("Problems during {} operation on job with id {}", action.toString(), ex); + } + } + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/358aef72/core/src/main/java/org/apache/syncope/core/rest/controller/ReportController.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/syncope/core/rest/controller/ReportController.java b/core/src/main/java/org/apache/syncope/core/rest/controller/ReportController.java index 8d5e3f7..0f8d4bf 100644 --- a/core/src/main/java/org/apache/syncope/core/rest/controller/ReportController.java +++ b/core/src/main/java/org/apache/syncope/core/rest/controller/ReportController.java @@ -45,6 +45,9 @@ import org.apache.syncope.common.types.ReportExecExportFormat; import org.apache.syncope.common.types.ReportExecStatus; import org.apache.syncope.common.types.ClientExceptionType; import org.apache.syncope.common.SyncopeClientException; +import org.apache.syncope.common.to.AbstractExecTO; +import org.apache.syncope.common.types.JobAction; +import org.apache.syncope.common.types.JobStatusType; import org.apache.syncope.core.init.JobInstanceLoader; import org.apache.syncope.core.persistence.beans.Report; import org.apache.syncope.core.persistence.beans.ReportExec; @@ -59,13 +62,12 @@ import org.apache.xmlgraphics.util.MimeConstants; import org.quartz.JobKey; import org.quartz.Scheduler; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.quartz.SchedulerFactoryBean; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @Component -public class ReportController extends AbstractTransactionalController<ReportTO> { +public class ReportController extends AbstractJobController<ReportTO> { @Autowired private ReportDAO reportDAO; @@ -77,9 +79,6 @@ public class ReportController extends AbstractTransactionalController<ReportTO> private JobInstanceLoader jobInstanceLoader; @Autowired - private SchedulerFactoryBean scheduler; - - @Autowired private ReportDataBinder binder; @PreAuthorize("hasRole('REPORT_CREATE')") @@ -341,4 +340,25 @@ public class ReportController extends AbstractTransactionalController<ReportTO> throw new UnresolvedReferenceException(); } + + @Override + @PreAuthorize("hasRole('REPORT_LIST')") + public <E extends AbstractExecTO> List<E> list(JobStatusType type, Class<E> reference) { + return super.list(type, reference); + } + + @PreAuthorize("hasRole('REPORT_EXECUTE')") + public void process(JobAction action, Long reportId) { + Report report = reportDAO.find(reportId); + if (report == null) { + throw new NotFoundException("Report " + reportId); + } + String jobName = JobInstanceLoader.getJobName(report); + process(action, jobName); + } + + @Override + protected Long getIdFromJobName(JobKey jobKey) { + return JobInstanceLoader.getReportIdFromJobName(jobKey.getName()); + } } http://git-wip-us.apache.org/repos/asf/syncope/blob/358aef72/core/src/main/java/org/apache/syncope/core/rest/controller/TaskController.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/syncope/core/rest/controller/TaskController.java b/core/src/main/java/org/apache/syncope/core/rest/controller/TaskController.java index 431f6be..b493069 100644 --- a/core/src/main/java/org/apache/syncope/core/rest/controller/TaskController.java +++ b/core/src/main/java/org/apache/syncope/core/rest/controller/TaskController.java @@ -35,6 +35,9 @@ import org.apache.syncope.common.types.PropagationTaskExecStatus; import org.apache.syncope.common.types.ClientExceptionType; import org.apache.syncope.common.types.TaskType; import org.apache.syncope.common.SyncopeClientException; +import org.apache.syncope.common.to.AbstractExecTO; +import org.apache.syncope.common.types.JobAction; +import org.apache.syncope.common.types.JobStatusType; import org.apache.syncope.core.init.ImplementationClassNamesLoader; import org.apache.syncope.core.init.JobInstanceLoader; import org.apache.syncope.core.notification.NotificationJob; @@ -60,7 +63,7 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Component; @Component -public class TaskController extends AbstractTransactionalController<AbstractTaskTO> { +public class TaskController extends AbstractJobController<AbstractTaskTO> { @Autowired private TaskDAO taskDAO; @@ -401,4 +404,25 @@ public class TaskController extends AbstractTransactionalController<AbstractTask throw new UnresolvedReferenceException(); } + + @Override + @PreAuthorize("hasRole('TASK_LIST')") + public <E extends AbstractExecTO> List<E> list(JobStatusType type, Class<E> reference) { + return super.list(type, reference); + } + + @PreAuthorize("hasRole('TASK_EXECUTE')") + public void process(JobAction action, Long taskId) { + Task task = taskDAO.find(taskId); + if (task == null) { + throw new NotFoundException("Task " + taskId); + } + String jobName = JobInstanceLoader.getJobName(task); + process(action, jobName); + } + + @Override + protected Long getIdFromJobName(JobKey jobKey) { + return JobInstanceLoader.getTaskIdFromJobName(jobKey.getName()); + } } http://git-wip-us.apache.org/repos/asf/syncope/blob/358aef72/core/src/main/java/org/apache/syncope/core/services/ReportServiceImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/syncope/core/services/ReportServiceImpl.java b/core/src/main/java/org/apache/syncope/core/services/ReportServiceImpl.java index 05ec25b..a4176fb 100644 --- a/core/src/main/java/org/apache/syncope/core/services/ReportServiceImpl.java +++ b/core/src/main/java/org/apache/syncope/core/services/ReportServiceImpl.java @@ -29,6 +29,8 @@ import org.apache.syncope.common.services.ReportService; import org.apache.syncope.common.reqres.PagedResult; import org.apache.syncope.common.to.ReportExecTO; import org.apache.syncope.common.to.ReportTO; +import org.apache.syncope.common.types.JobAction; +import org.apache.syncope.common.types.JobStatusType; import org.apache.syncope.common.types.RESTHeaders; import org.apache.syncope.common.types.ReportExecExportFormat; import org.apache.syncope.common.util.CollectionWrapper; @@ -41,10 +43,10 @@ import org.springframework.stereotype.Service; @Service public class ReportServiceImpl extends AbstractServiceImpl implements ReportService { - + @Autowired private ReportController controller; - + @Override public Response create(final ReportTO reportTO) { ReportTO createdReportTO = controller.create(reportTO); @@ -53,55 +55,55 @@ public class ReportServiceImpl extends AbstractServiceImpl implements ReportServ header(RESTHeaders.RESOURCE_ID.toString(), createdReportTO.getId()). build(); } - + @Override public void update(final Long reportId, final ReportTO reportTO) { reportTO.setId(reportId); controller.update(reportTO); } - + @Override public PagedResult<ReportTO> list() { return list(DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, null); } - + @Override public PagedResult<ReportTO> list(final String orderBy) { return list(DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, orderBy); } - + @Override public PagedResult<ReportTO> list(final Integer page, final Integer size) { return list(page, size, null); } - + @Override public PagedResult<ReportTO> list(final Integer page, final Integer size, final String orderBy) { List<OrderByClause> orderByClauses = getOrderByClauses(orderBy); return buildPagedResult(controller.list(page, size, orderByClauses), page, size, controller.count()); } - + @Override public List<ReportletConfClass> getReportletConfClasses() { return CollectionWrapper.wrap(controller.getReportletConfClasses(), ReportletConfClass.class); } - + @Override public ReportTO read(final Long reportId) { return controller.read(reportId); } - + @Override public ReportExecTO readExecution(final Long executionId) { return controller.readExecution(executionId); } - + @Override public Response exportExecutionResult(final Long executionId, final ReportExecExportFormat fmt) { final ReportExecExportFormat format = (fmt == null) ? ReportExecExportFormat.XML : fmt; final ReportExec reportExec = controller.getAndCheckReportExec(executionId); StreamingOutput sout = new StreamingOutput() { - + @Override public void write(final OutputStream os) throws IOException { controller.exportExecutionResult(os, reportExec, format); @@ -113,19 +115,29 @@ public class ReportServiceImpl extends AbstractServiceImpl implements ReportServ header(HttpHeaders.CONTENT_DISPOSITION, disposition). build(); } - + @Override public ReportExecTO execute(final Long reportId) { return controller.execute(reportId); } - + @Override public void delete(final Long reportId) { controller.delete(reportId); } - + @Override public void deleteExecution(final Long executionId) { controller.deleteExecution(executionId); } + + @Override + public List<ReportExecTO> list(JobStatusType type) { + return controller.list(type, ReportExecTO.class); + } + + @Override + public void process(JobAction action, Long reportId) { + controller.process(action, reportId); + } } http://git-wip-us.apache.org/repos/asf/syncope/blob/358aef72/core/src/main/java/org/apache/syncope/core/services/TaskServiceImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/syncope/core/services/TaskServiceImpl.java b/core/src/main/java/org/apache/syncope/core/services/TaskServiceImpl.java index 82a4d43..51b140a 100644 --- a/core/src/main/java/org/apache/syncope/core/services/TaskServiceImpl.java +++ b/core/src/main/java/org/apache/syncope/core/services/TaskServiceImpl.java @@ -34,8 +34,10 @@ import org.apache.syncope.common.to.TaskExecTO; import org.apache.syncope.common.to.AbstractTaskTO; import org.apache.syncope.common.reqres.PagedResult; import org.apache.syncope.common.to.PushTaskTO; +import org.apache.syncope.common.types.JobStatusType; import org.apache.syncope.common.types.RESTHeaders; import org.apache.syncope.common.types.PropagationTaskExecStatus; +import org.apache.syncope.common.types.JobAction; import org.apache.syncope.common.types.TaskType; import org.apache.syncope.common.util.CollectionWrapper; import org.apache.syncope.common.wrap.PushActionClass; @@ -155,4 +157,14 @@ public class TaskServiceImpl extends AbstractServiceImpl implements TaskService public BulkActionResult bulk(final BulkAction bulkAction) { return controller.bulk(bulkAction); } + + @Override + public List<TaskExecTO> list(JobStatusType type) { + return controller.list(type, TaskExecTO.class); + } + + @Override + public void process(JobAction action, Long taskId) { + controller.process(action, taskId); + } } http://git-wip-us.apache.org/repos/asf/syncope/blob/358aef72/core/src/test/java/org/apache/syncope/core/quartz/TestSampleJob.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/syncope/core/quartz/TestSampleJob.java b/core/src/test/java/org/apache/syncope/core/quartz/TestSampleJob.java new file mode 100644 index 0000000..f7387f0 --- /dev/null +++ b/core/src/test/java/org/apache/syncope/core/quartz/TestSampleJob.java @@ -0,0 +1,63 @@ +/* + * 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.quartz; + +import java.util.Date; +import org.apache.syncope.core.persistence.beans.SchedTask; +import org.apache.syncope.core.persistence.beans.TaskExec; +import org.quartz.JobExecutionException; + +/** + * Sample implementation for execution a scheduled task. + * + * @see SchedTask + */ +public class TestSampleJob extends AbstractTaskJob { + + @Override + protected String doExecute(final boolean dryRun) throws JobExecutionException { + if (!(task instanceof SchedTask)) { + throw new JobExecutionException("Task " + taskId + " isn't a SchedTask"); + } + + for (int i = 0; i < 10; i++) { + LOG.debug("TestSampleJob#doExecute round {} time {}", i, new Date().toString()); + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + throw new JobExecutionException("Job interrupted"); + } + } + + final SchedTask schedTask = (SchedTask) this.task; + + LOG.info("TestSampleJob {}running [SchedTask {}]", (dryRun + ? "dry " + : ""), schedTask.getId()); + + return (dryRun + ? "DRY " + : "") + "RUNNING"; + } + + @Override + protected boolean hasToBeRegistered(final TaskExec execution) { + return true; + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/358aef72/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java b/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java index 5823f9b..115f2c6 100644 --- a/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java +++ b/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java @@ -73,6 +73,8 @@ import org.apache.syncope.common.types.AttributeSchemaType; import org.apache.syncope.common.types.CipherAlgorithm; import org.apache.syncope.common.types.ConnConfProperty; import org.apache.syncope.common.types.IntMappingType; +import org.apache.syncope.common.types.JobAction; +import org.apache.syncope.common.types.JobStatusType; import org.apache.syncope.common.types.MappingPurpose; import org.apache.syncope.common.types.MatchingRule; import org.apache.syncope.common.types.PropagationTaskExecStatus; @@ -85,6 +87,7 @@ import org.apache.syncope.common.types.UnmatchingRule; import org.apache.syncope.common.util.CollectionWrapper; import org.apache.syncope.common.wrap.PushActionClass; import org.apache.syncope.common.wrap.ResourceName; +import org.apache.syncope.core.quartz.TestSampleJob; import org.apache.syncope.core.sync.TestSyncActions; import org.apache.syncope.core.sync.TestSyncRule; import org.apache.syncope.core.sync.impl.DBPasswordSyncActions; @@ -1444,4 +1447,61 @@ public class TaskTestITCase extends AbstractTest { NotificationTaskTO taskTO = findNotificationTaskBySender("[email protected]"); assertNotNull(taskTO); } + + @Test + public void issueSYNCOPE660() { + List<TaskExecTO> list = taskService.list(JobStatusType.ALL); + int old_size = list.size(); + + list = taskService.list(JobStatusType.SCHEDULED); + + SchedTaskTO task = new SchedTaskTO(); + task.setName("issueSYNCOPE660"); + task.setDescription("issueSYNCOPE660 Description"); + task.setJobClassName(TestSampleJob.class.getName()); + + Response response = taskService.create(task); + SchedTaskTO actual = getObject(response.getLocation(), TaskService.class, SchedTaskTO.class); + + list = taskService.list(JobStatusType.ALL); + assertEquals(list.size(), old_size + 1); + + taskService.process(JobAction.START, actual.getId()); + + int i = 0, maxit = 50; + + // wait for task exec completion (executions incremented) + do { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + } + + list = taskService.list(JobStatusType.RUNNING); + + assertNotNull(list); + i++; + } while (list.size() < 1 && i < maxit); + + assertEquals(list.size(), 1); + + taskService.process(JobAction.STOP, actual.getId()); + + i = 0; + + // wait for task exec completion (executions incremented) + do { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + } + + list = taskService.list(JobStatusType.RUNNING); + + assertNotNull(list); + i++; + } while (list.size() >= 1 && i < maxit); + + assertEquals(list.size(), 0); + } }
