You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2016/04/28 18:15:28 UTC

syncope git commit: Refactoring Task and Report common interface and logic

Repository: syncope
Updated Branches:
  refs/heads/master eed24866e -> e87e4102d


Refactoring Task and Report common interface and logic


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/e87e4102
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/e87e4102
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/e87e4102

Branch: refs/heads/master
Commit: e87e4102dac8b64954cbe1402cf462fd84a7e7a4
Parents: eed2486
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Thu Apr 28 18:15:21 2016 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Thu Apr 28 18:15:21 2016 +0200

----------------------------------------------------------------------
 .../client/console/rest/TaskRestClient.java     |   6 +-
 .../common/lib/types/StandardEntitlement.java   |   2 +
 .../common/rest/api/beans/ExecQuery.java        |  53 +++++++++
 .../common/rest/api/beans/TaskExecQuery.java    |  53 ---------
 .../rest/api/service/ExecutableService.java     | 117 +++++++++++++++++++
 .../common/rest/api/service/ReportService.java  |  73 +-----------
 .../common/rest/api/service/TaskService.java    |  83 +------------
 .../core/logic/AbstractExecutableLogic.java     |  48 ++++++++
 .../syncope/core/logic/AbstractJobLogic.java    |   4 +-
 .../syncope/core/logic/NotificationLogic.java   |   5 +-
 .../apache/syncope/core/logic/ReportLogic.java  |  42 ++++++-
 .../apache/syncope/core/logic/TaskLogic.java    |  15 ++-
 .../core/persistence/api/dao/ReportExecDAO.java |   5 +
 .../core/persistence/api/dao/TaskExecDAO.java   |   6 +-
 .../persistence/jpa/dao/JPAReportExecDAO.java   |  51 ++++++++
 .../cxf/service/AbstractExecutableService.java  |  85 ++++++++++++++
 .../rest/cxf/service/ReportServiceImpl.java     |  49 ++------
 .../core/rest/cxf/service/TaskServiceImpl.java  |  63 ++--------
 .../syncope/fit/core/PropagationTaskITCase.java |   5 +-
 .../syncope/fit/core/SchedTaskITCase.java       |   4 +-
 20 files changed, 440 insertions(+), 329 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/e87e4102/client/console/src/main/java/org/apache/syncope/client/console/rest/TaskRestClient.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/rest/TaskRestClient.java b/client/console/src/main/java/org/apache/syncope/client/console/rest/TaskRestClient.java
index 2a494cd..abc62a6 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/rest/TaskRestClient.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/rest/TaskRestClient.java
@@ -32,7 +32,7 @@ import org.apache.syncope.common.lib.to.ExecTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.TaskType;
 import org.apache.syncope.common.rest.api.beans.ExecuteQuery;
-import org.apache.syncope.common.rest.api.beans.TaskExecQuery;
+import org.apache.syncope.common.rest.api.beans.ExecQuery;
 import org.apache.syncope.common.rest.api.beans.TaskQuery;
 import org.apache.syncope.common.rest.api.service.TaskService;
 import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
@@ -63,7 +63,7 @@ public class TaskRestClient extends BaseRestClient implements ExecutionRestClien
 
     public int countExecutions(final String taskKey) {
         return getService(TaskService.class).
-                listExecutions(new TaskExecQuery.Builder().key(taskKey).page(1).size(1).build()).getTotalCount();
+                listExecutions(new ExecQuery.Builder().key(taskKey).page(1).size(1).build()).getTotalCount();
     }
 
     public List<PropagationTaskTO> listPropagationTasks(
@@ -141,7 +141,7 @@ public class TaskRestClient extends BaseRestClient implements ExecutionRestClien
 
     public List<ExecTO> listExecutions(final String taskKey, final int page, final int size) {
         return getService(TaskService.class).
-                listExecutions(new TaskExecQuery.Builder().key(taskKey).page(page).size(size).build()).getResult();
+                listExecutions(new ExecQuery.Builder().key(taskKey).page(page).size(size).build()).getResult();
     }
 
     private TaskType getTaskType(final Class<?> reference) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/e87e4102/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
index 81e3d33..e13eb29 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
@@ -198,6 +198,8 @@ public final class StandardEntitlement {
 
     public static final String NOTIFICATION_DELETE = "NOTIFICATION_DELETE";
 
+    public static final String NOTIFICATION_EXECUTE = "NOTIFICATION_EXECUTE";
+
     public static final String REPORT_TEMPLATE_LIST = "REPORT_TEMPLATE_LIST";
 
     public static final String REPORT_TEMPLATE_CREATE = "REPORT_TEMPLATE_CREATE";

http://git-wip-us.apache.org/repos/asf/syncope/blob/e87e4102/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ExecQuery.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ExecQuery.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ExecQuery.java
new file mode 100644
index 0000000..3b735a5
--- /dev/null
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ExecQuery.java
@@ -0,0 +1,53 @@
+/*
+ * 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.rest.api.beans;
+
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.PathParam;
+
+public class ExecQuery extends AbstractQuery {
+
+    private static final long serialVersionUID = -8792519310029596796L;
+
+    public static class Builder extends AbstractQuery.Builder<ExecQuery, Builder> {
+
+        @Override
+        protected ExecQuery newInstance() {
+            return new ExecQuery();
+        }
+
+        public Builder key(final String key) {
+            getInstance().setKey(key);
+            return this;
+        }
+    }
+
+    private String key;
+
+    public String getKey() {
+        return key;
+    }
+
+    @NotNull
+    @PathParam("key")
+    public void setKey(final String key) {
+        this.key = key;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/e87e4102/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/TaskExecQuery.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/TaskExecQuery.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/TaskExecQuery.java
deleted file mode 100644
index f7a9692..0000000
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/TaskExecQuery.java
+++ /dev/null
@@ -1,53 +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.common.rest.api.beans;
-
-import javax.validation.constraints.NotNull;
-import javax.ws.rs.PathParam;
-
-public class TaskExecQuery extends AbstractQuery {
-
-    private static final long serialVersionUID = -8792519310029596796L;
-
-    public static class Builder extends AbstractQuery.Builder<TaskExecQuery, Builder> {
-
-        @Override
-        protected TaskExecQuery newInstance() {
-            return new TaskExecQuery();
-        }
-
-        public Builder key(final String key) {
-            getInstance().setKey(key);
-            return this;
-        }
-    }
-
-    private String key;
-
-    public String getKey() {
-        return key;
-    }
-
-    @NotNull
-    @PathParam("key")
-    public void setKey(final String key) {
-        this.key = key;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/e87e4102/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ExecutableService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ExecutableService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ExecutableService.java
new file mode 100644
index 0000000..b97f252
--- /dev/null
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ExecutableService.java
@@ -0,0 +1,117 @@
+/*
+ * 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.rest.api.service;
+
+import java.util.List;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.BeanParam;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import org.apache.syncope.common.lib.to.BulkActionResult;
+import org.apache.syncope.common.lib.to.ExecTO;
+import org.apache.syncope.common.lib.to.JobTO;
+import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.lib.types.JobAction;
+import org.apache.syncope.common.rest.api.beans.BulkExecDeleteQuery;
+import org.apache.syncope.common.rest.api.beans.ExecQuery;
+import org.apache.syncope.common.rest.api.beans.ExecuteQuery;
+
+public interface ExecutableService extends JAXRSService {
+
+    /**
+     * Returns a paged list of executions matching the given query.
+     *
+     * @param query query conditions
+     * @return paged list of executions the given query
+     */
+    @GET
+    @Path("{key}/executions")
+    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    PagedResult<ExecTO> listExecutions(@BeanParam ExecQuery query);
+
+    /**
+     * Returns the list of recently completed executions, ordered by end date descendent.
+     *
+     * @param max the maximum number of executions to return
+     * @return list of recently completed executions, ordered by end date descendent
+     */
+    @GET
+    @Path("executions/recent")
+    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    List<ExecTO> listRecentExecutions(@Min(1) @QueryParam(JAXRSService.PARAM_MAX) @DefaultValue("25") int max);
+
+    /**
+     * Deletes the executable execution matching the provided key.
+     *
+     * @param executionKey key of executable execution to be deleted
+     */
+    @DELETE
+    @Path("executions/{executionKey}")
+    void deleteExecution(@NotNull @PathParam("executionKey") String executionKey);
+
+    /**
+     * Deletes the executions belonging matching the given query.
+     *
+     * @param query query conditions
+     * @return bulk action result
+     */
+    @DELETE
+    @Path("{key}/executions")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    BulkActionResult deleteExecutions(@BeanParam BulkExecDeleteQuery query);
+
+    /**
+     * Executes the executable matching the given query.
+     *
+     * @param query query conditions
+     * @return execution report for the executable matching the given query
+     */
+    @POST
+    @Path("{key}/execute")
+    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    ExecTO execute(@BeanParam ExecuteQuery query);
+
+    /**
+     * List jobs (running and / or scheduled).
+     *
+     * @return jobs (running and / or scheduled)
+     */
+    @GET
+    @Path("jobs")
+    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    List<JobTO> listJobs();
+
+    /**
+     * Executes an action on an existing executable's job.
+     *
+     * @param key executable key
+     * @param action action to execute
+     */
+    @POST
+    @Path("jobs/{key}")
+    void actionJob(@NotNull @PathParam("key") String key, @QueryParam("action") JobAction action);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/e87e4102/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReportService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReportService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReportService.java
index 2fceb88..cf79682 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReportService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReportService.java
@@ -19,12 +19,9 @@
 package org.apache.syncope.common.rest.api.service;
 
 import java.util.List;
-import javax.validation.constraints.Min;
 import javax.validation.constraints.NotNull;
-import javax.ws.rs.BeanParam;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
-import javax.ws.rs.DefaultValue;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
@@ -34,20 +31,14 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import org.apache.syncope.common.lib.to.BulkActionResult;
-import org.apache.syncope.common.lib.to.ExecTO;
-import org.apache.syncope.common.lib.to.JobTO;
 import org.apache.syncope.common.lib.to.ReportTO;
-import org.apache.syncope.common.lib.types.JobAction;
 import org.apache.syncope.common.lib.types.ReportExecExportFormat;
-import org.apache.syncope.common.rest.api.beans.BulkExecDeleteQuery;
-import org.apache.syncope.common.rest.api.beans.ExecuteQuery;
 
 /**
  * REST operations for reports.
  */
 @Path("reports")
-public interface ReportService extends JAXRSService {
+public interface ReportService extends ExecutableService {
 
     /**
      * Returns report with matching key.
@@ -99,48 +90,6 @@ public interface ReportService extends JAXRSService {
     void delete(@NotNull @PathParam("key") String key);
 
     /**
-     * Returns the list of recently completed report executions, ordered by end date descendent.
-     *
-     * @param max the maximum number of executions to return
-     * @return list of recently completed report executions, ordered by end date descendent
-     */
-    @GET
-    @Path("executions/recent")
-    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
-    List<ExecTO> listRecentExecutions(@Min(1) @QueryParam(JAXRSService.PARAM_MAX) @DefaultValue("25") int max);
-
-    /**
-     * Deletes report execution with matching key.
-     *
-     * @param executionKey key of execution report to be deleted
-     */
-    @DELETE
-    @Path("executions/{executionKey}")
-    void deleteExecution(@NotNull @PathParam("executionKey") String executionKey);
-
-    /**
-     * Deletes the report executions belonging matching the given query.
-     *
-     * @param query query conditions
-     * @return bulk action result
-     */
-    @DELETE
-    @Path("{key}/executions")
-    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    BulkActionResult deleteExecutions(@BeanParam BulkExecDeleteQuery query);
-
-    /**
-     * Executes the report matching the given query.
-     *
-     * @param query query conditions
-     * @return execution report for the report matching the given query
-     */
-    @POST
-    @Path("{key}/execute")
-    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
-    ExecTO execute(@BeanParam ExecuteQuery query);
-
-    /**
      * Exports the report execution with matching key in the requested format.
      *
      * @param executionKey key of execution report to be selected
@@ -153,24 +102,4 @@ public interface ReportService extends JAXRSService {
     Response exportExecutionResult(
             @NotNull @PathParam("executionKey") String executionKey,
             @QueryParam("format") ReportExecExportFormat fmt);
-
-    /**
-     * List report jobs (running and / or scheduled).
-     *
-     * @return report jobs (running and / or scheduled)
-     */
-    @GET
-    @Path("jobs")
-    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
-    List<JobTO> listJobs();
-
-    /**
-     * Executes an action on an existing report's job.
-     *
-     * @param key report key
-     * @param action action to execute
-     */
-    @POST
-    @Path("jobs/{key}")
-    void actionJob(@NotNull @PathParam("key") String key, @QueryParam("action") JobAction action);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/e87e4102/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/TaskService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/TaskService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/TaskService.java
index c1727d3..0d1bf41 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/TaskService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/TaskService.java
@@ -18,8 +18,6 @@
  */
 package org.apache.syncope.common.rest.api.service;
 
-import java.util.List;
-import javax.validation.constraints.Min;
 import javax.validation.constraints.NotNull;
 import javax.ws.rs.BeanParam;
 import javax.ws.rs.Consumes;
@@ -37,21 +35,15 @@ import javax.ws.rs.core.Response;
 import org.apache.syncope.common.lib.to.AbstractTaskTO;
 import org.apache.syncope.common.lib.to.BulkAction;
 import org.apache.syncope.common.lib.to.BulkActionResult;
-import org.apache.syncope.common.lib.to.ExecTO;
-import org.apache.syncope.common.lib.to.JobTO;
 import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.lib.to.SchedTaskTO;
-import org.apache.syncope.common.lib.types.JobAction;
-import org.apache.syncope.common.rest.api.beans.BulkExecDeleteQuery;
-import org.apache.syncope.common.rest.api.beans.ExecuteQuery;
-import org.apache.syncope.common.rest.api.beans.TaskExecQuery;
 import org.apache.syncope.common.rest.api.beans.TaskQuery;
 
 /**
  * REST operations for tasks.
  */
 @Path("tasks")
-public interface TaskService extends JAXRSService {
+public interface TaskService extends ExecutableService {
 
     /**
      * Returns the task matching the given key.
@@ -109,59 +101,6 @@ public interface TaskService extends JAXRSService {
     void delete(@NotNull @PathParam("key") String key);
 
     /**
-     * Returns a paged list of task executions matching the given query.
-     *
-     * @param query query conditions
-     * @return paged list of task executions the given query
-     */
-    @GET
-    @Path("{key}/executions")
-    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
-    PagedResult<ExecTO> listExecutions(@BeanParam TaskExecQuery query);
-
-    /**
-     * Returns the list of recently completed task executions, ordered by end date descendent.
-     *
-     * @param max the maximum number of executions to return
-     * @return list of recently completed task executions, ordered by end date descendent
-     */
-    @GET
-    @Path("executions/recent")
-    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
-    List<ExecTO> listRecentExecutions(@Min(1) @QueryParam(JAXRSService.PARAM_MAX) @DefaultValue("25") int max);
-
-    /**
-     * Deletes the task execution matching the provided key.
-     *
-     * @param executionKey key of task execution to be deleted
-     */
-    @DELETE
-    @Path("executions/{executionKey}")
-    void deleteExecution(@NotNull @PathParam("executionKey") String executionKey);
-
-    /**
-     * Deletes the task executions belonging matching the given query.
-     *
-     * @param query query conditions
-     * @return bulk action result
-     */
-    @DELETE
-    @Path("{key}/executions")
-    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    BulkActionResult deleteExecutions(@BeanParam BulkExecDeleteQuery query);
-
-    /**
-     * Executes the task matching the given query.
-     *
-     * @param query query conditions
-     * @return execution report for the task matching the given query
-     */
-    @POST
-    @Path("{key}/execute")
-    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
-    ExecTO execute(@BeanParam ExecuteQuery query);
-
-    /**
      * Executes the provided bulk action.
      *
      * @param bulkAction list of task ids against which the bulk action will be performed.
@@ -172,24 +111,4 @@ public interface TaskService extends JAXRSService {
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     BulkActionResult bulk(@NotNull BulkAction bulkAction);
-
-    /**
-     * List task jobs (running and / or scheduled).
-     *
-     * @return task jobs (running and / or scheduled)
-     */
-    @GET
-    @Path("jobs")
-    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
-    List<JobTO> listJobs();
-
-    /**
-     * Executes an action on an existing task's job.
-     *
-     * @param key task key
-     * @param action action to execute
-     */
-    @POST
-    @Path("jobs/{key}")
-    void actionJob(@NotNull @PathParam("key") String key, @QueryParam("action") JobAction action);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/e87e4102/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractExecutableLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractExecutableLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractExecutableLogic.java
new file mode 100644
index 0000000..8a7d677
--- /dev/null
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractExecutableLogic.java
@@ -0,0 +1,48 @@
+/*
+ * 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.logic;
+
+import java.util.Date;
+import java.util.List;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+import org.apache.syncope.common.lib.to.BulkActionResult;
+import org.apache.syncope.common.lib.to.ExecTO;
+import org.apache.syncope.common.lib.to.JobTO;
+import org.apache.syncope.common.lib.types.JobAction;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+
+public abstract class AbstractExecutableLogic<T extends AbstractBaseBean> extends AbstractJobLogic<T> {
+
+    public abstract ExecTO execute(String key, Date startAt, boolean dryRun);
+
+    public abstract int countExecutions(String key);
+
+    public abstract List<ExecTO> listExecutions(String key, int page, int size, List<OrderByClause> orderByClauses);
+
+    public abstract List<ExecTO> listRecentExecutions(int max);
+
+    public abstract ExecTO deleteExecution(String executionKey);
+
+    public abstract BulkActionResult deleteExecutions(
+            String key, Date startedBefore, Date startedAfter, Date endedBefore, Date endedAfter);
+
+    public abstract List<JobTO> listJobs();
+
+    public abstract void actionJob(String key, JobAction action);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/e87e4102/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractJobLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractJobLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractJobLogic.java
index ac4af1c..2473f6d 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractJobLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractJobLogic.java
@@ -44,7 +44,7 @@ abstract class AbstractJobLogic<T extends AbstractBaseBean> extends AbstractTran
 
     protected abstract Triple<JobType, String, String> getReference(final JobKey jobKey);
 
-    protected List<JobTO> listJobs() {
+    protected List<JobTO> doListJobs() {
         List<JobTO> jobTOs = new ArrayList<>();
 
         try {
@@ -80,7 +80,7 @@ abstract class AbstractJobLogic<T extends AbstractBaseBean> extends AbstractTran
         return jobTOs;
     }
 
-    protected void actionJob(final JobKey jobKey, final JobAction action) {
+    protected void doActionJob(final JobKey jobKey, final JobAction action) {
         try {
             if (scheduler.getScheduler().checkExists(jobKey)) {
                 switch (action) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/e87e4102/core/logic/src/main/java/org/apache/syncope/core/logic/NotificationLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/NotificationLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/NotificationLogic.java
index 95597f0..aa54c0d 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/NotificationLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/NotificationLogic.java
@@ -115,12 +115,13 @@ public class NotificationLogic extends AbstractJobLogic<NotificationTO> {
 
     @PreAuthorize("hasRole('" + StandardEntitlement.NOTIFICATION_LIST + "')")
     public JobTO getJob() {
-        List<JobTO> jobs = super.listJobs();
+        List<JobTO> jobs = super.doListJobs();
         return jobs.isEmpty() ? null : jobs.get(0);
     }
 
+    @PreAuthorize("hasRole('" + StandardEntitlement.NOTIFICATION_EXECUTE + "')")
     public void actionJob(final JobAction action) {
-        super.actionJob(JobManager.NOTIFICATION_JOB, action);
+        super.doActionJob(JobManager.NOTIFICATION_JOB, action);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/e87e4102/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
index 8b2eb1d..3fd8ccf 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
@@ -62,6 +62,7 @@ import org.apache.syncope.common.lib.types.JobType;
 import org.apache.syncope.common.lib.types.StandardEntitlement;
 import org.apache.syncope.core.logic.report.XSLTTransformer;
 import org.apache.syncope.core.persistence.api.dao.ConfDAO;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.xmlgraphics.util.MimeConstants;
 import org.quartz.JobKey;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -69,7 +70,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Component;
 
 @Component
-public class ReportLogic extends AbstractJobLogic<ReportTO> {
+public class ReportLogic extends AbstractExecutableLogic<ReportTO> {
 
     @Autowired
     private ConfDAO confDAO;
@@ -156,7 +157,8 @@ public class ReportLogic extends AbstractJobLogic<ReportTO> {
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.REPORT_EXECUTE + "')")
-    public ExecTO execute(final String key, final Date startAt) {
+    @Override
+    public ExecTO execute(final String key, final Date startAt, final boolean dryRun) {
         Report report = reportDAO.find(key);
         if (report == null) {
             throw new NotFoundException("Report " + key);
@@ -297,7 +299,34 @@ public class ReportLogic extends AbstractJobLogic<ReportTO> {
         return deletedReport;
     }
 
+    @PreAuthorize("hasRole('" + StandardEntitlement.REPORT_READ + "')")
+    @Override
+    public int countExecutions(final String key) {
+        return reportExecDAO.count(key);
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.REPORT_READ + "')")
+    @Override
+    public List<ExecTO> listExecutions(
+            final String key, final int page, final int size, final List<OrderByClause> orderByClauses) {
+
+        Report report = reportDAO.find(key);
+        if (report == null) {
+            throw new NotFoundException("Report " + key);
+        }
+
+        return CollectionUtils.collect(reportExecDAO.findAll(report, page, size, orderByClauses),
+                new Transformer<ReportExec, ExecTO>() {
+
+            @Override
+            public ExecTO transform(final ReportExec reportExec) {
+                return binder.getExecTO(reportExec);
+            }
+        }, new ArrayList<ExecTO>());
+    }
+
     @PreAuthorize("hasRole('" + StandardEntitlement.REPORT_LIST + "')")
+    @Override
     public List<ExecTO> listRecentExecutions(final int max) {
         return CollectionUtils.collect(reportExecDAO.findRecent(max), new Transformer<ReportExec, ExecTO>() {
 
@@ -309,6 +338,7 @@ public class ReportLogic extends AbstractJobLogic<ReportTO> {
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.REPORT_DELETE + "')")
+    @Override
     public ExecTO deleteExecution(final String executionKey) {
         ReportExec reportExec = reportExecDAO.find(executionKey);
         if (reportExec == null) {
@@ -321,6 +351,7 @@ public class ReportLogic extends AbstractJobLogic<ReportTO> {
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.REPORT_DELETE + "')")
+    @Override
     public BulkActionResult deleteExecutions(
             final String key,
             final Date startedBefore, final Date startedAfter, final Date endedBefore, final Date endedAfter) {
@@ -355,20 +386,21 @@ public class ReportLogic extends AbstractJobLogic<ReportTO> {
                 : Triple.of(JobType.REPORT, key, binder.buildRefDesc(report));
     }
 
-    @Override
     @PreAuthorize("hasRole('" + StandardEntitlement.REPORT_LIST + "')")
+    @Override
     public List<JobTO> listJobs() {
-        return super.listJobs();
+        return super.doListJobs();
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.REPORT_EXECUTE + "')")
+    @Override
     public void actionJob(final String key, final JobAction action) {
         Report report = reportDAO.find(key);
         if (report == null) {
             throw new NotFoundException("Report " + key);
         }
 
-        actionJob(JobNamer.getJobKey(report), action);
+        doActionJob(JobNamer.getJobKey(report), action);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/e87e4102/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
index 9d88b43..8136e46 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
@@ -65,7 +65,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Component;
 
 @Component
-public class TaskLogic extends AbstractJobLogic<AbstractTaskTO> {
+public class TaskLogic extends AbstractExecutableLogic<AbstractTaskTO> {
 
     @Autowired
     private TaskDAO taskDAO;
@@ -191,6 +191,7 @@ public class TaskLogic extends AbstractJobLogic<AbstractTaskTO> {
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.TASK_EXECUTE + "')")
+    @Override
     public ExecTO execute(final String key, final Date startAt, final boolean dryRun) {
         Task task = taskDAO.find(key);
         if (task == null) {
@@ -276,11 +277,13 @@ public class TaskLogic extends AbstractJobLogic<AbstractTaskTO> {
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.TASK_READ + "')")
+    @Override
     public int countExecutions(final String key) {
         return taskExecDAO.count(key);
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.TASK_READ + "')")
+    @Override
     public List<ExecTO> listExecutions(
             final String key, final int page, final int size, final List<OrderByClause> orderByClauses) {
 
@@ -300,6 +303,7 @@ public class TaskLogic extends AbstractJobLogic<AbstractTaskTO> {
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.TASK_LIST + "')")
+    @Override
     public List<ExecTO> listRecentExecutions(final int max) {
         return CollectionUtils.collect(taskExecDAO.findRecent(max), new Transformer<TaskExec, ExecTO>() {
 
@@ -311,6 +315,7 @@ public class TaskLogic extends AbstractJobLogic<AbstractTaskTO> {
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.TASK_DELETE + "')")
+    @Override
     public ExecTO deleteExecution(final String execKey) {
         TaskExec taskExec = taskExecDAO.find(execKey);
         if (taskExec == null) {
@@ -323,6 +328,7 @@ public class TaskLogic extends AbstractJobLogic<AbstractTaskTO> {
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.TASK_DELETE + "')")
+    @Override
     public BulkActionResult deleteExecutions(
             final String key,
             final Date startedBefore, final Date startedAfter, final Date endedBefore, final Date endedAfter) {
@@ -357,20 +363,21 @@ public class TaskLogic extends AbstractJobLogic<AbstractTaskTO> {
                 : Triple.of(JobType.TASK, key, binder.buildRefDesc(task));
     }
 
-    @Override
     @PreAuthorize("hasRole('" + StandardEntitlement.TASK_LIST + "')")
+    @Override
     public List<JobTO> listJobs() {
-        return super.listJobs();
+        return super.doListJobs();
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.TASK_EXECUTE + "')")
+    @Override
     public void actionJob(final String key, final JobAction action) {
         Task task = taskDAO.find(key);
         if (task == null) {
             throw new NotFoundException("Task " + key);
         }
 
-        actionJob(JobNamer.getJobKey(task), action);
+        doActionJob(JobNamer.getJobKey(task), action);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/e87e4102/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ReportExecDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ReportExecDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ReportExecDAO.java
index f4c1fcf..61c7cfc 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ReportExecDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ReportExecDAO.java
@@ -20,6 +20,7 @@ package org.apache.syncope.core.persistence.api.dao;
 
 import java.util.Date;
 import java.util.List;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.entity.Report;
 import org.apache.syncope.core.persistence.api.entity.ReportExec;
 
@@ -33,6 +34,10 @@ public interface ReportExecDAO extends DAO<ReportExec> {
 
     ReportExec findLatestEnded(Report report);
 
+    int count(String reportKey);
+
+    List<ReportExec> findAll(Report report, int page, int itemsPerPage, List<OrderByClause> orderByClauses);
+
     List<ReportExec> findAll(Report report, Date startedBefore, Date startedAfter, Date endedBefore, Date endedAfter);
 
     ReportExec save(ReportExec execution);

http://git-wip-us.apache.org/repos/asf/syncope/blob/e87e4102/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/TaskExecDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/TaskExecDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/TaskExecDAO.java
index 703ad74..e90cc8d 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/TaskExecDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/TaskExecDAO.java
@@ -34,13 +34,13 @@ public interface TaskExecDAO extends DAO<TaskExec> {
 
     <T extends Task> TaskExec findLatestEnded(T task);
 
-    <T extends Task> List<TaskExec> findAll(
-            T task, Date startedBefore, Date startedAfter, Date endedBefore, Date endedAfter);
-
     int count(String taskKey);
 
     <T extends Task> List<TaskExec> findAll(T task, int page, int itemsPerPage, List<OrderByClause> orderByClauses);
 
+    <T extends Task> List<TaskExec> findAll(
+            T task, Date startedBefore, Date startedAfter, Date endedBefore, Date endedAfter);
+
     TaskExec save(TaskExec execution);
 
     void saveAndAdd(String taskKey, TaskExec execution);

http://git-wip-us.apache.org/repos/asf/syncope/blob/e87e4102/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAReportExecDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAReportExecDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAReportExecDAO.java
index d5b801e..de8f5ec 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAReportExecDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAReportExecDAO.java
@@ -20,13 +20,16 @@ package org.apache.syncope.core.persistence.jpa.dao;
 
 import java.util.Date;
 import java.util.List;
+import javax.persistence.Query;
 import javax.persistence.TypedQuery;
 import org.apache.syncope.core.persistence.api.dao.ReportExecDAO;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.entity.Report;
 import org.apache.syncope.core.persistence.api.entity.ReportExec;
 import org.apache.syncope.core.persistence.jpa.entity.JPAReportExec;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.ReflectionUtils;
 
 @Repository
 public class JPAReportExecDAO extends AbstractDAO<ReportExec> implements ReportExecDAO {
@@ -70,6 +73,54 @@ public class JPAReportExecDAO extends AbstractDAO<ReportExec> implements ReportE
     }
 
     @Override
+    public int count(final String reportKey) {
+        Query countQuery = entityManager().createNativeQuery(
+                "SELECT COUNT(e.id) FROM " + JPAReportExec.TABLE + " e WHERE e.report_id=?1");
+        countQuery.setParameter(1, reportKey);
+
+        return ((Number) countQuery.getSingleResult()).intValue();
+    }
+
+    private String toOrderByStatement(final List<OrderByClause> orderByClauses) {
+        StringBuilder statement = new StringBuilder();
+
+        for (OrderByClause clause : orderByClauses) {
+            String field = clause.getField().trim();
+            if (ReflectionUtils.findField(JPAReportExec.class, field) != null) {
+                statement.append("e.").append(field).append(' ').append(clause.getDirection().name());
+            }
+        }
+
+        if (statement.length() == 0) {
+            statement.append("ORDER BY e.id DESC");
+        } else {
+            statement.insert(0, "ORDER BY ");
+        }
+        return statement.toString();
+    }
+
+    @Override
+    public List<ReportExec> findAll(final Report report,
+            final int page, final int itemsPerPage, final List<OrderByClause> orderByClauses) {
+
+        String queryString =
+                "SELECT e FROM " + JPAReportExec.class.getSimpleName() + " e WHERE e.report=:report "
+                + toOrderByStatement(orderByClauses);
+
+        TypedQuery<ReportExec> query = entityManager().createQuery(queryString, ReportExec.class);
+        query.setParameter("report", report);
+
+        // page starts from 1, while setFirtResult() starts from 0
+        query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
+
+        if (itemsPerPage >= 0) {
+            query.setMaxResults(itemsPerPage);
+        }
+
+        return query.getResultList();
+    }
+
+    @Override
     public List<ReportExec> findAll(
             final Report report,
             final Date startedBefore, final Date startedAfter, final Date endedBefore, final Date endedAfter) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/e87e4102/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractExecutableService.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractExecutableService.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractExecutableService.java
new file mode 100644
index 0000000..c6a1917
--- /dev/null
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractExecutableService.java
@@ -0,0 +1,85 @@
+/*
+ * 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.cxf.service;
+
+import java.util.List;
+import org.apache.syncope.common.lib.to.BulkActionResult;
+import org.apache.syncope.common.lib.to.ExecTO;
+import org.apache.syncope.common.lib.to.JobTO;
+import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.lib.types.JobAction;
+import org.apache.syncope.common.rest.api.beans.BulkExecDeleteQuery;
+import org.apache.syncope.common.rest.api.beans.ExecQuery;
+import org.apache.syncope.common.rest.api.beans.ExecuteQuery;
+import org.apache.syncope.common.rest.api.service.ExecutableService;
+import org.apache.syncope.core.logic.AbstractExecutableLogic;
+
+public abstract class AbstractExecutableService extends AbstractServiceImpl implements ExecutableService {
+
+    protected abstract AbstractExecutableLogic<?> getExecutableLogic();
+
+    @Override
+    public PagedResult<ExecTO> listExecutions(final ExecQuery query) {
+        return buildPagedResult(
+                getExecutableLogic().listExecutions(
+                        query.getKey(),
+                        query.getPage(),
+                        query.getSize(),
+                        getOrderByClauses(query.getOrderBy())),
+                query.getPage(),
+                query.getSize(),
+                getExecutableLogic().countExecutions(query.getKey()));
+    }
+
+    @Override
+    public List<ExecTO> listRecentExecutions(final int max) {
+        return getExecutableLogic().listRecentExecutions(max);
+    }
+
+    @Override
+    public void deleteExecution(final String executionKey) {
+        getExecutableLogic().deleteExecution(executionKey);
+    }
+
+    @Override
+    public BulkActionResult deleteExecutions(final BulkExecDeleteQuery query) {
+        return getExecutableLogic().deleteExecutions(
+                query.getKey(),
+                query.getStartedBefore(),
+                query.getStartedAfter(),
+                query.getEndedBefore(),
+                query.getEndedAfter());
+    }
+
+    @Override
+    public ExecTO execute(final ExecuteQuery query) {
+        return getExecutableLogic().execute(query.getKey(), query.getStartAt(), query.getDryRun());
+    }
+
+    @Override
+    public List<JobTO> listJobs() {
+        return getExecutableLogic().listJobs();
+    }
+
+    @Override
+    public void actionJob(final String key, final JobAction action) {
+        getExecutableLogic().actionJob(key, action);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/e87e4102/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReportServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReportServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReportServiceImpl.java
index b68b2a5..0826b99 100644
--- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReportServiceImpl.java
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReportServiceImpl.java
@@ -25,28 +25,28 @@ import java.util.List;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.StreamingOutput;
-import org.apache.syncope.common.lib.to.BulkActionResult;
-import org.apache.syncope.common.lib.to.JobTO;
-import org.apache.syncope.common.lib.to.ExecTO;
 import org.apache.syncope.common.lib.to.ReportTO;
-import org.apache.syncope.common.lib.types.JobAction;
 import org.apache.syncope.common.lib.types.ReportExecExportFormat;
 import org.apache.syncope.common.rest.api.RESTHeaders;
-import org.apache.syncope.common.rest.api.beans.BulkExecDeleteQuery;
-import org.apache.syncope.common.rest.api.beans.ExecuteQuery;
 import org.apache.syncope.common.rest.api.service.ReportService;
+import org.apache.syncope.core.logic.AbstractExecutableLogic;
 import org.apache.syncope.core.logic.ReportLogic;
 import org.apache.syncope.core.persistence.api.entity.ReportExec;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 @Service
-public class ReportServiceImpl extends AbstractServiceImpl implements ReportService {
+public class ReportServiceImpl extends AbstractExecutableService implements ReportService {
 
     @Autowired
     private ReportLogic logic;
 
     @Override
+    protected AbstractExecutableLogic<?> getExecutableLogic() {
+        return logic;
+    }
+
+    @Override
     public Response create(final ReportTO reportTO) {
         ReportTO createdReportTO = logic.create(reportTO);
         URI location = uriInfo.getAbsolutePathBuilder().path(String.valueOf(createdReportTO.getKey())).build();
@@ -88,42 +88,7 @@ public class ReportServiceImpl extends AbstractServiceImpl implements ReportServ
     }
 
     @Override
-    public ExecTO execute(final ExecuteQuery query) {
-        return logic.execute(query.getKey(), query.getStartAt());
-    }
-
-    @Override
     public void delete(final String key) {
         logic.delete(key);
     }
-
-    @Override
-    public List<ExecTO> listRecentExecutions(final int size) {
-        return logic.listRecentExecutions(size);
-    }
-
-    @Override
-    public void deleteExecution(final String executionKey) {
-        logic.deleteExecution(executionKey);
-    }
-
-    @Override
-    public BulkActionResult deleteExecutions(final BulkExecDeleteQuery query) {
-        return logic.deleteExecutions(
-                query.getKey(),
-                query.getStartedBefore(),
-                query.getStartedAfter(),
-                query.getEndedBefore(),
-                query.getEndedAfter());
-    }
-
-    @Override
-    public List<JobTO> listJobs() {
-        return logic.listJobs();
-    }
-
-    @Override
-    public void actionJob(final String key, final JobAction action) {
-        logic.actionJob(key, action);
-    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/e87e4102/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/TaskServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/TaskServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/TaskServiceImpl.java
index 1bae984..16ba72a 100644
--- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/TaskServiceImpl.java
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/TaskServiceImpl.java
@@ -19,36 +19,35 @@
 package org.apache.syncope.core.rest.cxf.service;
 
 import java.net.URI;
-import java.util.List;
 import javax.ws.rs.BadRequestException;
 import javax.ws.rs.core.Response;
 import org.apache.syncope.common.lib.to.AbstractTaskTO;
 import org.apache.syncope.common.lib.to.BulkAction;
 import org.apache.syncope.common.lib.to.BulkActionResult;
-import org.apache.syncope.common.lib.to.JobTO;
 import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.lib.to.PushTaskTO;
 import org.apache.syncope.common.lib.to.SchedTaskTO;
 import org.apache.syncope.common.lib.to.PullTaskTO;
-import org.apache.syncope.common.lib.to.ExecTO;
-import org.apache.syncope.common.lib.types.JobAction;
 import org.apache.syncope.common.rest.api.RESTHeaders;
-import org.apache.syncope.common.rest.api.beans.BulkExecDeleteQuery;
-import org.apache.syncope.common.rest.api.beans.ExecuteQuery;
-import org.apache.syncope.common.rest.api.beans.TaskExecQuery;
 import org.apache.syncope.common.rest.api.beans.TaskQuery;
 import org.apache.syncope.common.rest.api.service.TaskService;
+import org.apache.syncope.core.logic.AbstractExecutableLogic;
 import org.apache.syncope.core.logic.TaskLogic;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 @Service
-public class TaskServiceImpl extends AbstractServiceImpl implements TaskService {
+public class TaskServiceImpl extends AbstractExecutableService implements TaskService {
 
     @Autowired
     private TaskLogic logic;
 
     @Override
+    protected AbstractExecutableLogic<?> getExecutableLogic() {
+        return logic;
+    }
+
+    @Override
     public Response create(final SchedTaskTO taskTO) {
         SchedTaskTO createdTask;
         if (taskTO instanceof PullTaskTO || taskTO instanceof PushTaskTO || taskTO instanceof SchedTaskTO) {
@@ -68,11 +67,6 @@ public class TaskServiceImpl extends AbstractServiceImpl implements TaskService
         logic.delete(key);
     }
 
-    @Override
-    public ExecTO execute(final ExecuteQuery query) {
-        return logic.execute(query.getKey(), query.getStartAt(), query.getDryRun());
-    }
-
     @SuppressWarnings("unchecked")
     @Override
     public <T extends AbstractTaskTO> PagedResult<T> list(final TaskQuery query) {
@@ -112,39 +106,6 @@ public class TaskServiceImpl extends AbstractServiceImpl implements TaskService
     }
 
     @Override
-    public PagedResult<ExecTO> listExecutions(final TaskExecQuery query) {
-        return buildPagedResult(
-                logic.listExecutions(
-                        query.getKey(),
-                        query.getPage(),
-                        query.getSize(),
-                        getOrderByClauses(query.getOrderBy())),
-                query.getPage(),
-                query.getSize(),
-                logic.countExecutions(query.getKey()));
-    }
-
-    @Override
-    public List<ExecTO> listRecentExecutions(final int max) {
-        return logic.listRecentExecutions(max);
-    }
-
-    @Override
-    public void deleteExecution(final String executionKey) {
-        logic.deleteExecution(executionKey);
-    }
-
-    @Override
-    public BulkActionResult deleteExecutions(final BulkExecDeleteQuery query) {
-        return logic.deleteExecutions(
-                query.getKey(),
-                query.getStartedBefore(),
-                query.getStartedAfter(),
-                query.getEndedBefore(),
-                query.getEndedAfter());
-    }
-
-    @Override
     public BulkActionResult bulk(final BulkAction bulkAction) {
         BulkActionResult result = new BulkActionResult();
 
@@ -189,14 +150,4 @@ public class TaskServiceImpl extends AbstractServiceImpl implements TaskService
 
         return result;
     }
-
-    @Override
-    public List<JobTO> listJobs() {
-        return logic.listJobs();
-    }
-
-    @Override
-    public void actionJob(final String key, final JobAction action) {
-        logic.actionJob(key, action);
-    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/e87e4102/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java
index e32eafb..4008a08 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java
@@ -34,7 +34,7 @@ import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.TaskType;
 import org.apache.syncope.common.rest.api.beans.ExecuteQuery;
-import org.apache.syncope.common.rest.api.beans.TaskExecQuery;
+import org.apache.syncope.common.rest.api.beans.ExecQuery;
 import org.apache.syncope.common.rest.api.beans.TaskQuery;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -142,8 +142,7 @@ public class PropagationTaskITCase extends AbstractTaskITCase {
         assertFalse(task.getExecutions().isEmpty());
 
         // check list executions
-        PagedResult<ExecTO> execs = taskService.listExecutions(
-                new TaskExecQuery.Builder().key("1e697572-b896-484c-ae7f-0c8f63fcbc6c").
+        PagedResult<ExecTO> execs = taskService.listExecutions(new ExecQuery.Builder().key("1e697572-b896-484c-ae7f-0c8f63fcbc6c").
                 page(1).size(2).build());
         assertTrue(execs.getTotalCount() >= execs.getResult().size());
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/e87e4102/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SchedTaskITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SchedTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SchedTaskITCase.java
index 30cc8c8..fcaf479 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SchedTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SchedTaskITCase.java
@@ -42,7 +42,7 @@ import org.apache.syncope.common.lib.to.ExecTO;
 import org.apache.syncope.common.lib.types.JobAction;
 import org.apache.syncope.common.lib.types.TaskType;
 import org.apache.syncope.common.rest.api.beans.ExecuteQuery;
-import org.apache.syncope.common.rest.api.beans.TaskExecQuery;
+import org.apache.syncope.common.rest.api.beans.ExecQuery;
 import org.apache.syncope.common.rest.api.beans.TaskQuery;
 import org.apache.syncope.common.rest.api.service.TaskService;
 import org.apache.syncope.fit.core.reference.TestSampleJobDelegate;
@@ -124,7 +124,7 @@ public class SchedTaskITCase extends AbstractTaskITCase {
         } while (task.getExecutions().isEmpty() && i < maxit);
 
         PagedResult<ExecTO> execs =
-                taskService.listExecutions(new TaskExecQuery.Builder().key(task.getKey()).build());
+                taskService.listExecutions(new ExecQuery.Builder().key(task.getKey()).build());
         assertEquals(1, execs.getTotalCount());
         assertTrue(execs.getResult().get(0).getStart().after(initial));
         // round 1 sec for safety