syncope-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From giacom...@apache.org
Subject [1/4] syncope git commit: [SYNCOPE-660] Asynchronous jobs delegated to Quartz, like Task and Report, now can be checked and interrupted during execution
Date Fri, 22 May 2015 07:47:42 GMT
Repository: syncope
Updated Branches:
  refs/heads/master d19133ef5 -> d489e8c59


[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/master
Commit: 358aef72dca5672361cf1a9a3ac2b8c0c57517a3
Parents: 1d6451b
Author: giacomolm <giacomolm@hotmail.it>
Authored: Tue Apr 14 16:33:45 2015 +0200
Committer: giacomolm <giacomolm@hotmail.it>
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("syncope648@syncope.apache.org");
         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);
+    }
 }


Mime
View raw message