http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyService.java
----------------------------------------------------------------------
diff --git 
a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyService.java
 
b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyService.java
index 7bb78d8..eb3f9aa 100644
--- 
a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyService.java
+++ 
b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyService.java
@@ -43,8 +43,6 @@ import org.apache.syncope.common.lib.patch.AssociationPatch;
 import org.apache.syncope.common.lib.patch.DeassociationPatch;
 import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.common.lib.to.AttrTO;
-import org.apache.syncope.common.lib.to.BulkAction;
-import org.apache.syncope.common.lib.to.BulkActionResult;
 import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.lib.to.ProvisioningResult;
 import org.apache.syncope.common.lib.types.ResourceAssociationAction;
@@ -190,7 +188,7 @@ public interface AnyService<TO extends AnyTO> extends 
JAXRSService {
      * Executes resource-related operations on given entity.
      *
      * @param patch external resources to be used for propagation-related 
operations
-     * @return Response object featuring BulkActionResult as Entity
+     * @return batch results as Response entity
      */
     @Parameter(name = RESTHeaders.PREFER, in = ParameterIn.HEADER,
             description = "Allows client to specify a preference for the 
result to be returned from the server",
@@ -212,9 +210,7 @@ public interface AnyService<TO extends AnyTO> extends 
JAXRSService {
             @Schema(implementation = ResourceDeassociationAction.class))
     @ApiResponses({
         @ApiResponse(responseCode = "200",
-                description = "Bulk action result", content =
-                @Content(schema =
-                        @Schema(implementation = BulkActionResult.class))),
+                description = "Batch results available, returned as Response 
entity"),
         @ApiResponse(responseCode = "204",
                 description = "No content if 'Prefer: return-no-content' was 
specified", headers =
                 @Header(name = RESTHeaders.PREFERENCE_APPLIED, schema =
@@ -226,15 +222,15 @@ public interface AnyService<TO extends AnyTO> extends 
JAXRSService {
                 + " date of the entity") })
     @POST
     @Path("{key}/deassociate/{action}")
-    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML })
     @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML })
+    @Produces(RESTHeaders.MULTIPART_MIXED)
     Response deassociate(@NotNull DeassociationPatch patch);
 
     /**
      * Executes resource-related operations on given entity.
      *
      * @param patch external resources to be used for propagation-related 
operations
-     * @return Response object featuring BulkActionResult as Entity
+     * @return batch results as Response entity
      */
     @Parameter(name = RESTHeaders.PREFER, in = ParameterIn.HEADER,
             description = "Allows client to specify a preference for the 
result to be returned from the server",
@@ -256,9 +252,7 @@ public interface AnyService<TO extends AnyTO> extends 
JAXRSService {
             @Schema(implementation = ResourceAssociationAction.class))
     @ApiResponses({
         @ApiResponse(responseCode = "200",
-                description = "Bulk action result", content =
-                @Content(schema =
-                        @Schema(implementation = BulkActionResult.class))),
+                description = "Batch results available, returned as Response 
entity"),
         @ApiResponse(responseCode = "204",
                 description = "No content if 'Prefer: return-no-content' was 
specified", headers =
                 @Header(name = RESTHeaders.PREFERENCE_APPLIED, schema =
@@ -270,39 +264,7 @@ public interface AnyService<TO extends AnyTO> extends 
JAXRSService {
                 + " date of the entity") })
     @POST
     @Path("{key}/associate/{action}")
-    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML })
     @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML })
+    @Produces(RESTHeaders.MULTIPART_MIXED)
     Response associate(@NotNull AssociationPatch patch);
-
-    /**
-     * Executes the provided bulk action.
-     *
-     * @param bulkAction list of any object ids against which the bulk action 
will be performed.
-     * @return Response object featuring BulkActionResult as Entity
-     */
-    @Parameter(name = RESTHeaders.PREFER, in = ParameterIn.HEADER,
-            description = "Allows client to specify a preference for the 
result to be returned from the server",
-            allowEmptyValue = true, schema =
-            @Schema(defaultValue = "return-content", allowableValues = { 
"return-content", "return-no-content" }))
-    @Parameter(name = RESTHeaders.NULL_PRIORITY_ASYNC, in = ParameterIn.HEADER,
-            description = "If 'true', instructs the propagation process not to 
wait for completion when communicating"
-            + " with External Resources with no priority set",
-            allowEmptyValue = true, schema =
-            @Schema(type = "boolean", defaultValue = "false"))
-    @ApiResponses({
-        @ApiResponse(responseCode = "200",
-                description = "Bulk action result", content =
-                @Content(schema =
-                        @Schema(implementation = BulkActionResult.class))),
-        @ApiResponse(responseCode = "204",
-                description = "No content if 'Prefer: return-no-content' was 
specified", headers =
-                @Header(name = RESTHeaders.PREFERENCE_APPLIED, schema =
-                        @Schema(type = "string"),
-                        description = "Allows the server to inform the "
-                        + "client about the fact that a specified preference 
was applied")) })
-    @POST
-    @Path("bulk")
-    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML })
-    @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML })
-    Response bulk(@NotNull BulkAction bulkAction);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/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
index 416bcdc..808aae8 100644
--- 
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
@@ -33,13 +33,13 @@ 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 javax.ws.rs.core.Response;
 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.RESTHeaders;
-import org.apache.syncope.common.rest.api.beans.BulkExecDeleteQuery;
+import org.apache.syncope.common.rest.api.beans.ExecDeleteQuery;
 import org.apache.syncope.common.rest.api.beans.ExecQuery;
 import org.apache.syncope.common.rest.api.beans.ExecuteQuery;
 
@@ -83,12 +83,15 @@ public interface ExecutableService extends JAXRSService {
      * Deletes the executions belonging matching the given query.
      *
      * @param query query conditions
-     * @return bulk action result
+     * @return batch results as Response entity
      */
     @DELETE
+    @ApiResponses(
+            @ApiResponse(responseCode = "200",
+                    description = "Batch results available, returned as 
Response entity"))
     @Path("{key}/executions")
-    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML })
-    BulkActionResult deleteExecutions(@BeanParam BulkExecDeleteQuery query);
+    @Produces(RESTHeaders.MULTIPART_MIXED)
+    Response deleteExecutions(@BeanParam ExecDeleteQuery query);
 
     /**
      * Executes the executable matching the given query.

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/GroupService.java
----------------------------------------------------------------------
diff --git 
a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/GroupService.java
 
b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/GroupService.java
index 8b7d893..05c2ba7 100644
--- 
a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/GroupService.java
+++ 
b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/GroupService.java
@@ -46,7 +46,7 @@ import org.apache.syncope.common.lib.to.ExecTO;
 import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.lib.to.ProvisioningResult;
-import org.apache.syncope.common.lib.types.BulkMembersActionType;
+import org.apache.syncope.common.lib.types.ProvisionAction;
 import org.apache.syncope.common.rest.api.RESTHeaders;
 import org.apache.syncope.common.rest.api.beans.AnyQuery;
 
@@ -212,13 +212,13 @@ public interface GroupService extends AnyService<GroupTO> 
{
      * (De)provision all members of the given group from / onto all the 
resources associated to it.
      *
      * @param key group key
-     * @param actionType action type to perform on all group members
+     * @param action action type to perform on all group members
      * @return execution report for the task generated on purpose
      */
     @POST
-    @Path("{key}/members/{actionType}")
+    @Path("{key}/members/{action}")
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML })
-    ExecTO bulkMembersAction(
+    ExecTO provisionMembers(
             @NotNull @PathParam("key") String key,
-            @NotNull @PathParam("actionType") BulkMembersActionType 
actionType);
+            @NotNull @PathParam("action") ProvisionAction action);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReconciliationService.java
----------------------------------------------------------------------
diff --git 
a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReconciliationService.java
 
b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReconciliationService.java
index 68e4179..11bffe0 100644
--- 
a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReconciliationService.java
+++ 
b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReconciliationService.java
@@ -38,7 +38,7 @@ import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.rest.api.RESTHeaders;
 
 /**
- * REST operations for tasks.
+ * REST operations for reconciliation.
  */
 @Tag(name = "Reconciliation")
 @SecurityRequirements({

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceService.java
----------------------------------------------------------------------
diff --git 
a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceService.java
 
b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceService.java
index 6aaa3a8..27b7813 100644
--- 
a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceService.java
+++ 
b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceService.java
@@ -41,12 +41,9 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import org.apache.syncope.common.lib.patch.ResourceDeassociationPatch;
-import org.apache.syncope.common.lib.to.BulkActionResult;
 import org.apache.syncope.common.lib.to.ConnObjectTO;
 import org.apache.syncope.common.lib.to.PagedConnObjectTOResult;
 import org.apache.syncope.common.lib.to.ResourceTO;
-import org.apache.syncope.common.lib.types.ResourceDeassociationAction;
 import org.apache.syncope.common.rest.api.RESTHeaders;
 import org.apache.syncope.common.rest.api.beans.ConnObjectTOListQuery;
 
@@ -204,22 +201,4 @@ public interface ResourceService extends JAXRSService {
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML })
     @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML })
     void check(@NotNull ResourceTO resourceTO);
-
-    /**
-     * De-associate any objects from the given resource.
-     *
-     * @param patch any objects to be used for propagation-related operations
-     * @return Bulk action result
-     */
-    @Parameter(name = "key", description = "Resource's key", in = 
ParameterIn.PATH, schema =
-            @Schema(type = "string"))
-    @Parameter(name = "anyTypeKey", description = "AnyType's key", in = 
ParameterIn.PATH, schema =
-            @Schema(type = "string"))
-    @Parameter(name = "action", description = "Deassociation action", in = 
ParameterIn.PATH, schema =
-            @Schema(implementation = ResourceDeassociationAction.class))
-    @POST
-    @Path("{key}/bulkDeassociation/{anyTypeKey}/{action}")
-    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML })
-    @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML })
-    BulkActionResult bulkDeassociation(@NotNull ResourceDeassociationPatch 
patch);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SyncopeService.java
----------------------------------------------------------------------
diff --git 
a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SyncopeService.java
 
b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SyncopeService.java
index 26332e8..9e5ff71 100644
--- 
a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SyncopeService.java
+++ 
b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SyncopeService.java
@@ -124,7 +124,7 @@ public interface SyncopeService extends JAXRSService {
     /**
      * Gets batch results, in case asynchronous was requested.
      *
-     * @return batch results returned as Response entity
+     * @return batch results as Response entity
      */
     @GET
     @ApiResponses({

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/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 70f5935..07a18d8 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
@@ -43,8 +43,6 @@ import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.syncope.common.lib.to.TaskTO;
-import org.apache.syncope.common.lib.to.BulkAction;
-import org.apache.syncope.common.lib.to.BulkActionResult;
 import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.lib.to.SchedTaskTO;
 import org.apache.syncope.common.lib.types.TaskType;
@@ -140,16 +138,4 @@ public interface TaskService extends ExecutableService {
     @Path("{type}/{key}")
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML })
     void delete(@NotNull @PathParam("type") TaskType type, @NotNull 
@PathParam("key") String key);
-
-    /**
-     * Executes the provided bulk action.
-     *
-     * @param bulkAction list of task ids against which the bulk action will 
be performed.
-     * @return Bulk action result
-     */
-    @POST
-    @Path("bulk")
-    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML })
-    @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML })
-    BulkActionResult bulk(@NotNull BulkAction bulkAction);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/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
index 6b34bc7..a25325d 100644
--- 
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
@@ -22,10 +22,10 @@ import java.util.Date;
 import java.util.List;
 import org.apache.commons.lang3.tuple.Pair;
 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.common.rest.api.batch.BatchResponseItem;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 
 public abstract class AbstractExecutableLogic<T extends AbstractBaseBean> 
extends AbstractJobLogic<T> {
@@ -39,7 +39,7 @@ public abstract class AbstractExecutableLogic<T extends 
AbstractBaseBean> extend
 
     public abstract ExecTO deleteExecution(String executionKey);
 
-    public abstract BulkActionResult deleteExecutions(
+    public abstract List<BatchResponseItem> deleteExecutions(
             String key, Date startedBefore, Date startedAfter, Date 
endedBefore, Date endedAfter);
 
     public abstract JobTO getJob(String key);

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
----------------------------------------------------------------------
diff --git 
a/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java 
b/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
index 8193745..5524fd7 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
@@ -40,7 +40,7 @@ import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.common.lib.to.PropagationStatus;
 import org.apache.syncope.common.lib.to.ProvisioningResult;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.apache.syncope.common.lib.types.BulkMembersActionType;
+import org.apache.syncope.common.lib.types.ProvisionAction;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.common.lib.types.ImplementationEngine;
 import org.apache.syncope.common.lib.types.ImplementationType;
@@ -384,7 +384,7 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, 
GroupPatch> {
     @PreAuthorize("hasRole('" + StandardEntitlement.TASK_CREATE + "') "
             + "and hasRole('" + StandardEntitlement.TASK_EXECUTE + "')")
     @Transactional
-    public ExecTO bulkMembersAction(final String key, final 
BulkMembersActionType actionType) {
+    public ExecTO provisionMembers(final String key, final ProvisionAction 
action) {
         Group group = groupDAO.find(key);
         if (group == null) {
             throw new NotFoundException("Group " + key);
@@ -392,18 +392,19 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, 
GroupPatch> {
 
         Implementation jobDelegate = 
implementationDAO.find(ImplementationType.TASKJOB_DELEGATE).stream().
                 filter(impl -> 
GroupMemberProvisionTaskJobDelegate.class.getName().equals(impl.getBody())).
-                findFirst().orElse(null);
-        if (jobDelegate == null) {
-            jobDelegate = entityFactory.newEntity(Implementation.class);
-            
jobDelegate.setKey(GroupMemberProvisionTaskJobDelegate.class.getSimpleName());
-            jobDelegate.setEngine(ImplementationEngine.JAVA);
-            jobDelegate.setType(ImplementationType.TASKJOB_DELEGATE);
-            
jobDelegate.setBody(GroupMemberProvisionTaskJobDelegate.class.getName());
-            jobDelegate = implementationDAO.save(jobDelegate);
-        }
+                findFirst().orElseGet(() -> {
+                    Implementation caz = 
entityFactory.newEntity(Implementation.class);
+                    
caz.setKey(GroupMemberProvisionTaskJobDelegate.class.getSimpleName());
+                    caz.setEngine(ImplementationEngine.JAVA);
+                    caz.setType(ImplementationType.TASKJOB_DELEGATE);
+                    
caz.setBody(GroupMemberProvisionTaskJobDelegate.class.getName());
+                    caz = implementationDAO.save(caz);
+                    return caz;
+                });
 
         SchedTask task = entityFactory.newEntity(SchedTask.class);
-        task.setName("Bulk member provision for group " + group.getName());
+        task.setName((action == ProvisionAction.DEPROVISION ? "de" : "")
+                + "provision members of group " + group.getName());
         task.setActive(true);
         task.setJobDelegate(jobDelegate);
         task = taskDAO.save(task);
@@ -416,7 +417,7 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, 
GroupPatch> {
 
             jobDataMap.put(TaskJob.DRY_RUN_JOBDETAIL_KEY, false);
             
jobDataMap.put(GroupMemberProvisionTaskJobDelegate.GROUP_KEY_JOBDETAIL_KEY, 
key);
-            
jobDataMap.put(GroupMemberProvisionTaskJobDelegate.ACTION_TYPE_JOBDETAIL_KEY, 
actionType);
+            
jobDataMap.put(GroupMemberProvisionTaskJobDelegate.ACTION_JOBDETAIL_KEY, 
action);
 
             scheduler.getScheduler().triggerJob(
                     JobNamer.getJobKey(task),

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/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 523b75a..95aa956 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
@@ -22,12 +22,15 @@ import java.io.ByteArrayInputStream;
 import java.io.OutputStream;
 import java.lang.reflect.Method;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
 import java.util.zip.ZipInputStream;
+import javax.ws.rs.core.Response;
 import javax.xml.transform.stream.StreamSource;
 import org.apache.cocoon.pipeline.NonCachingPipeline;
 import org.apache.cocoon.pipeline.Pipeline;
@@ -39,7 +42,6 @@ import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.commons.lang3.tuple.Triple;
 import org.apache.syncope.common.lib.SyncopeClientException;
-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;
@@ -49,6 +51,8 @@ import org.apache.syncope.common.lib.types.JobType;
 import org.apache.syncope.common.lib.types.ReportExecExportFormat;
 import org.apache.syncope.common.lib.types.ReportExecStatus;
 import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.batch.BatchResponseItem;
 import org.apache.syncope.core.logic.cocoon.FopSerializer;
 import org.apache.syncope.core.logic.cocoon.TextSerializer;
 import org.apache.syncope.core.logic.cocoon.XSLTTransformer;
@@ -62,6 +66,7 @@ import org.apache.syncope.core.persistence.api.entity.Report;
 import org.apache.syncope.core.persistence.api.entity.ReportExec;
 import org.apache.syncope.core.provisioning.api.data.ReportDataBinder;
 import org.apache.syncope.core.provisioning.api.job.JobNamer;
+import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
 import org.apache.xmlgraphics.util.MimeConstants;
 import org.quartz.JobKey;
 import org.quartz.SchedulerException;
@@ -332,7 +337,7 @@ public class ReportLogic extends 
AbstractExecutableLogic<ReportTO> {
 
     @PreAuthorize("hasRole('" + StandardEntitlement.REPORT_DELETE + "')")
     @Override
-    public BulkActionResult deleteExecutions(
+    public List<BatchResponseItem> deleteExecutions(
             final String key,
             final Date startedBefore, final Date startedAfter, final Date 
endedBefore, final Date endedAfter) {
 
@@ -341,19 +346,24 @@ public class ReportLogic extends 
AbstractExecutableLogic<ReportTO> {
             throw new NotFoundException("Report " + key);
         }
 
-        BulkActionResult result = new BulkActionResult();
+        List<BatchResponseItem> batchResponseItems = new ArrayList<>();
 
         reportExecDAO.findAll(report, startedBefore, startedAfter, 
endedBefore, endedAfter).forEach(exec -> {
+            BatchResponseItem item = new BatchResponseItem();
+            item.getHeaders().put(RESTHeaders.RESOURCE_KEY, 
Arrays.asList(exec.getKey()));
+            batchResponseItems.add(item);
+
             try {
                 reportExecDAO.delete(exec);
-                result.getResults().put(String.valueOf(exec.getKey()), 
BulkActionResult.Status.SUCCESS);
+                item.setStatus(Response.Status.OK.getStatusCode());
             } catch (Exception e) {
                 LOG.error("Error deleting execution {} of report {}", 
exec.getKey(), key, e);
-                result.getResults().put(String.valueOf(exec.getKey()), 
BulkActionResult.Status.FAILURE);
+                item.setStatus(Response.Status.BAD_REQUEST.getStatusCode());
+                item.setContent(ExceptionUtils2.getFullStackTrace(e));
             }
         });
 
-        return result;
+        return batchResponseItems;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/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 526fb63..17f7376 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
@@ -19,16 +19,18 @@
 package org.apache.syncope.core.logic;
 
 import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
+import javax.ws.rs.core.Response;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.commons.lang3.tuple.Triple;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.TaskTO;
-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.PropagationTaskTO;
@@ -39,6 +41,8 @@ import org.apache.syncope.common.lib.types.JobAction;
 import org.apache.syncope.common.lib.types.JobType;
 import org.apache.syncope.common.lib.types.StandardEntitlement;
 import org.apache.syncope.common.lib.types.TaskType;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.batch.BatchResponseItem;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.dao.TaskDAO;
 import org.apache.syncope.core.persistence.api.dao.TaskExecDAO;
@@ -56,6 +60,7 @@ import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
 import org.apache.syncope.core.persistence.api.dao.NotificationDAO;
 import 
org.apache.syncope.core.provisioning.api.notification.NotificationJobDelegate;
+import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
 import org.apache.syncope.core.provisioning.java.job.TaskJob;
 import org.quartz.JobDataMap;
 import org.quartz.JobKey;
@@ -349,28 +354,36 @@ public class TaskLogic extends 
AbstractExecutableLogic<TaskTO> {
 
     @PreAuthorize("hasRole('" + StandardEntitlement.TASK_DELETE + "')")
     @Override
-    public BulkActionResult deleteExecutions(
+    public List<BatchResponseItem> deleteExecutions(
             final String key,
-            final Date startedBefore, final Date startedAfter, final Date 
endedBefore, final Date endedAfter) {
+            final Date startedBefore,
+            final Date startedAfter,
+            final Date endedBefore,
+            final Date endedAfter) {
 
         Task task = taskDAO.find(key);
         if (task == null) {
             throw new NotFoundException("Task " + key);
         }
 
-        BulkActionResult result = new BulkActionResult();
+        List<BatchResponseItem> batchResponseItems = new ArrayList<>();
 
         taskExecDAO.findAll(task, startedBefore, startedAfter, endedBefore, 
endedAfter).forEach(exec -> {
+            BatchResponseItem item = new BatchResponseItem();
+            item.getHeaders().put(RESTHeaders.RESOURCE_KEY, 
Arrays.asList(exec.getKey()));
+            batchResponseItems.add(item);
+
             try {
                 taskExecDAO.delete(exec);
-                result.getResults().put(String.valueOf(exec.getKey()), 
BulkActionResult.Status.SUCCESS);
+                item.setStatus(Response.Status.OK.getStatusCode());
             } catch (Exception e) {
                 LOG.error("Error deleting execution {} of task {}", 
exec.getKey(), key, e);
-                result.getResults().put(String.valueOf(exec.getKey()), 
BulkActionResult.Status.FAILURE);
+                item.setStatus(Response.Status.BAD_REQUEST.getStatusCode());
+                item.setContent(ExceptionUtils2.getFullStackTrace(e));
             }
         });
 
-        return result;
+        return batchResponseItems;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PropagationTaskValidator.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PropagationTaskValidator.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PropagationTaskValidator.java
index 6c8a949..a50d105 100644
--- 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PropagationTaskValidator.java
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PropagationTaskValidator.java
@@ -22,7 +22,7 @@ import java.util.List;
 
 import javax.validation.ConstraintValidatorContext;
 import org.apache.syncope.common.lib.types.EntityViolationType;
-import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
+import org.apache.syncope.common.lib.types.ExecStatus;
 import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 
@@ -43,7 +43,7 @@ public class PropagationTaskValidator extends 
AbstractValidator<PropagationTaskC
                 List<? extends TaskExec> executions = task.getExecs();
                 for (TaskExec execution : executions) {
                     try {
-                        
PropagationTaskExecStatus.valueOf(execution.getStatus());
+                        ExecStatus.valueOf(execution.getStatus());
                     } catch (IllegalArgumentException e) {
                         LOG.error("Invalid execution status '" + 
execution.getStatus() + "'", e);
                         isValid = false;

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskExecTest.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskExecTest.java
 
b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskExecTest.java
index 2bb8e83..af257f5 100644
--- 
a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskExecTest.java
+++ 
b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskExecTest.java
@@ -24,7 +24,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.List;
-import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
+import org.apache.syncope.common.lib.types.ExecStatus;
 import org.apache.syncope.core.persistence.api.dao.TaskDAO;
 import org.apache.syncope.core.persistence.api.dao.TaskExecDAO;
 import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
@@ -78,7 +78,7 @@ public class TaskExecTest extends AbstractTest {
         TaskExec exec = entityFactory.newEntity(TaskExec.class);
         exec.setStart(new Date());
         exec.setEnd(new Date());
-        exec.setStatus(PropagationTaskExecStatus.SUCCESS.name());
+        exec.setStatus(ExecStatus.SUCCESS.name());
         exec.setMessage(faultyMessage);
 
         task.add(exec);

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/TaskTest.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/TaskTest.java
 
b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/TaskTest.java
index 0da3e39..d48cdf9 100644
--- 
a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/TaskTest.java
+++ 
b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/TaskTest.java
@@ -36,7 +36,7 @@ import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ImplementationEngine;
 import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.common.lib.types.MatchingRule;
-import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
+import org.apache.syncope.common.lib.types.ExecStatus;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.common.lib.types.PullMode;
 import org.apache.syncope.common.lib.types.TaskType;
@@ -150,7 +150,7 @@ public class TaskTest extends AbstractTest {
 
         TaskExec execution = entityFactory.newEntity(TaskExec.class);
         execution.setTask(task);
-        execution.setStatus(PropagationTaskExecStatus.CREATED.name());
+        execution.setStatus(ExecStatus.CREATED.name());
         execution.setStart(new Date());
         task.add(execution);
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationReporter.java
----------------------------------------------------------------------
diff --git 
a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationReporter.java
 
b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationReporter.java
index a27f02a..78ac2cc 100644
--- 
a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationReporter.java
+++ 
b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationReporter.java
@@ -22,7 +22,7 @@ import java.util.Collection;
 import java.util.List;
 import org.apache.syncope.common.lib.to.PropagationStatus;
 import org.apache.syncope.common.lib.to.PropagationTaskTO;
-import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
+import org.apache.syncope.common.lib.types.ExecStatus;
 import org.identityconnectors.framework.common.objects.ConnectorObject;
 
 /**
@@ -50,7 +50,7 @@ public interface PropagationReporter {
      */
     void onSuccessOrNonPriorityResourceFailures(
             PropagationTaskTO propagationTask,
-            PropagationTaskExecStatus execStatus,
+            ExecStatus execStatus,
             String failureReason,
             ConnectorObject beforeObj,
             ConnectorObject afterObj);

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/GroupMemberProvisionTaskJobDelegate.java
----------------------------------------------------------------------
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/GroupMemberProvisionTaskJobDelegate.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/GroupMemberProvisionTaskJobDelegate.java
index 10d48b9..2356cfb 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/GroupMemberProvisionTaskJobDelegate.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/GroupMemberProvisionTaskJobDelegate.java
@@ -23,7 +23,7 @@ import java.util.List;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.to.PropagationStatus;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.apache.syncope.common.lib.types.BulkMembersActionType;
+import org.apache.syncope.common.lib.types.ProvisionAction;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.search.MembershipCond;
@@ -41,7 +41,7 @@ import 
org.springframework.transaction.annotation.Transactional;
 
 public class GroupMemberProvisionTaskJobDelegate extends 
AbstractSchedTaskJobDelegate {
 
-    public static final String ACTION_TYPE_JOBDETAIL_KEY = "actionType";
+    public static final String ACTION_JOBDETAIL_KEY = "action";
 
     public static final String GROUP_KEY_JOBDETAIL_KEY = "groupKey";
 
@@ -59,7 +59,7 @@ public class GroupMemberProvisionTaskJobDelegate extends 
AbstractSchedTaskJobDel
 
     private String groupKey;
 
-    private BulkMembersActionType actionType;
+    private ProvisionAction action;
 
     @Transactional
     @Override
@@ -67,7 +67,7 @@ public class GroupMemberProvisionTaskJobDelegate extends 
AbstractSchedTaskJobDel
             throws JobExecutionException {
 
         groupKey = 
context.getMergedJobDataMap().getString(GROUP_KEY_JOBDETAIL_KEY);
-        actionType = (BulkMembersActionType) 
context.getMergedJobDataMap().get(ACTION_TYPE_JOBDETAIL_KEY);
+        action = (ProvisionAction) 
context.getMergedJobDataMap().get(ACTION_JOBDETAIL_KEY);
 
         super.execute(taskKey, dryRun, context);
     }
@@ -77,7 +77,7 @@ public class GroupMemberProvisionTaskJobDelegate extends 
AbstractSchedTaskJobDel
         Group group = groupDAO.authFind(groupKey);
 
         StringBuilder result = new StringBuilder("Group 
").append(group.getName()).append(" members ");
-        if (actionType == BulkMembersActionType.DEPROVISION) {
+        if (action == ProvisionAction.DEPROVISION) {
             result.append("de");
         }
         result.append("provision\n\n");
@@ -89,11 +89,11 @@ public class GroupMemberProvisionTaskJobDelegate extends 
AbstractSchedTaskJobDel
         List<User> users = 
searchDAO.search(SearchCond.getLeafCond(membershipCond), AnyTypeKind.USER);
         Collection<String> groupResourceKeys = 
groupDAO.findAllResourceKeys(groupKey);
         status.set("About to "
-                + (actionType == BulkMembersActionType.DEPROVISION ? "de" : 
"") + "provision "
+                + (action == ProvisionAction.DEPROVISION ? "de" : "") + 
"provision "
                 + users.size() + " users from " + groupResourceKeys);
 
         for (int i = 0; i < users.size() && !interrupt; i++) {
-            List<PropagationStatus> statuses = actionType == 
BulkMembersActionType.DEPROVISION
+            List<PropagationStatus> statuses = action == 
ProvisionAction.DEPROVISION
                     ? 
userProvisioningManager.deprovision(users.get(i).getKey(), groupResourceKeys, 
false)
                     : userProvisioningManager.provision(users.get(i).getKey(), 
true, null, groupResourceKeys, false);
             for (PropagationStatus propagationStatus : statuses) {
@@ -117,11 +117,11 @@ public class GroupMemberProvisionTaskJobDelegate extends 
AbstractSchedTaskJobDel
         membershipCond.setGroup(groupKey);
         List<AnyObject> anyObjects = 
searchDAO.search(SearchCond.getLeafCond(membershipCond), 
AnyTypeKind.ANY_OBJECT);
         status.set("About to "
-                + (actionType == BulkMembersActionType.DEPROVISION ? "de" : 
"") + "provision "
+                + (action == ProvisionAction.DEPROVISION ? "de" : "") + 
"provision "
                 + anyObjects.size() + " any objects from " + 
groupResourceKeys);
 
         for (int i = 0; i < anyObjects.size() && !interrupt; i++) {
-            List<PropagationStatus> statuses = actionType == 
BulkMembersActionType.DEPROVISION
+            List<PropagationStatus> statuses = action == 
ProvisionAction.DEPROVISION
                     ? 
anyObjectProvisioningManager.deprovision(anyObjects.get(i).getKey(), 
groupResourceKeys, false)
                     : 
anyObjectProvisioningManager.provision(anyObjects.get(i).getKey(), 
groupResourceKeys, false);
 
@@ -151,5 +151,4 @@ public class GroupMemberProvisionTaskJobDelegate extends 
AbstractSchedTaskJobDel
         // always record execution result
         return true;
     }
-
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
----------------------------------------------------------------------
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
index 4d7f00d..766f345 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
@@ -36,7 +36,7 @@ import org.apache.syncope.common.lib.to.ExecTO;
 import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AuditElements;
 import org.apache.syncope.common.lib.types.AuditElements.Result;
-import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
+import org.apache.syncope.common.lib.types.ExecStatus;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.common.lib.types.TraceLevel;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
@@ -377,7 +377,7 @@ public abstract class AbstractPropagationTaskExecutor 
implements PropagationTask
         Date start = new Date();
 
         TaskExec execution = entityFactory.newEntity(TaskExec.class);
-        execution.setStatus(PropagationTaskExecStatus.CREATED.name());
+        execution.setStatus(ExecStatus.CREATED.name());
 
         String taskExecutionMessage = null;
         String failureReason = null;
@@ -423,8 +423,8 @@ public abstract class AbstractPropagationTaskExecutor 
implements PropagationTask
             }
 
             execution.setStatus(propagationAttempted.get()
-                    ? PropagationTaskExecStatus.SUCCESS.name()
-                    : PropagationTaskExecStatus.NOT_ATTEMPTED.name());
+                    ? ExecStatus.SUCCESS.name()
+                    : ExecStatus.NOT_ATTEMPTED.name());
 
             LOG.debug("Successfully propagated to {}", task.getResource());
             result = Result.SUCCESS;
@@ -449,7 +449,7 @@ public abstract class AbstractPropagationTaskExecutor 
implements PropagationTask
             }
 
             try {
-                execution.setStatus(PropagationTaskExecStatus.FAILURE.name());
+                execution.setStatus(ExecStatus.FAILURE.name());
             } catch (Exception wft) {
                 LOG.error("While executing KO action on {}", execution, wft);
             }
@@ -503,9 +503,8 @@ public abstract class AbstractPropagationTaskExecutor 
implements PropagationTask
             }
 
             if (reporter != null) {
-                reporter.onSuccessOrNonPriorityResourceFailures(
-                        taskTO,
-                        
PropagationTaskExecStatus.valueOf(execution.getStatus()),
+                reporter.onSuccessOrNonPriorityResourceFailures(taskTO,
+                        ExecStatus.valueOf(execution.getStatus()),
                         failureReason,
                         beforeObj,
                         afterObj);
@@ -568,7 +567,7 @@ public abstract class AbstractPropagationTaskExecutor 
implements PropagationTask
     protected boolean hasToBeregistered(final PropagationTask task, final 
TaskExec execution) {
         boolean result;
 
-        boolean failed = 
PropagationTaskExecStatus.valueOf(execution.getStatus()) != 
PropagationTaskExecStatus.SUCCESS;
+        boolean failed = ExecStatus.valueOf(execution.getStatus()) != 
ExecStatus.SUCCESS;
 
         switch (task.getOperation()) {
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationReporter.java
----------------------------------------------------------------------
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationReporter.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationReporter.java
index 2932147..a2df73c 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationReporter.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationReporter.java
@@ -25,7 +25,7 @@ import java.util.List;
 import java.util.Optional;
 import org.apache.syncope.common.lib.to.PropagationStatus;
 import org.apache.syncope.common.lib.to.PropagationTaskTO;
-import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
+import org.apache.syncope.common.lib.types.ExecStatus;
 import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 import 
org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
 import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
@@ -48,7 +48,7 @@ public class DefaultPropagationReporter implements 
PropagationReporter {
     @Override
     public void onSuccessOrNonPriorityResourceFailures(
             final PropagationTaskTO taskTO,
-            final PropagationTaskExecStatus executionStatus,
+            final ExecStatus executionStatus,
             final String failureReason,
             final ConnectorObject beforeObj,
             final ConnectorObject afterObj) {
@@ -79,7 +79,7 @@ public class DefaultPropagationReporter implements 
PropagationReporter {
         if (propagationTask.isPresent()) {
             PropagationStatus status = new PropagationStatus();
             status.setResource(propagationTask.get().getResource());
-            status.setStatus(PropagationTaskExecStatus.FAILURE);
+            status.setStatus(ExecStatus.FAILURE);
             status.setFailureReason(
                     "Propagation error: " + failingResource + " priority 
resource failed to propagate.");
             add(status);

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PriorityPropagationTaskExecutor.java
----------------------------------------------------------------------
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PriorityPropagationTaskExecutor.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PriorityPropagationTaskExecutor.java
index be97bb5..adaa8f7 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PriorityPropagationTaskExecutor.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PriorityPropagationTaskExecutor.java
@@ -35,7 +35,7 @@ import java.util.function.Function;
 import java.util.stream.Collectors;
 import javax.annotation.Resource;
 import org.apache.syncope.common.lib.to.PropagationTaskTO;
-import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
+import org.apache.syncope.common.lib.types.ExecStatus;
 import 
org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
 import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
@@ -99,15 +99,15 @@ public class PriorityPropagationTaskExecutor extends 
AbstractPropagationTaskExec
         // first process priority resources sequentially and fail as soon as 
any propagation failure is reported
         prioritizedTasks.forEach(task -> {
             TaskExec execution = null;
-            PropagationTaskExecStatus execStatus;
+            ExecStatus execStatus;
             try {
                 execution = newPropagationTaskCallable(task, reporter).call();
-                execStatus = 
PropagationTaskExecStatus.valueOf(execution.getStatus());
+                execStatus = ExecStatus.valueOf(execution.getStatus());
             } catch (Exception e) {
                 LOG.error("Unexpected exception", e);
-                execStatus = PropagationTaskExecStatus.FAILURE;
+                execStatus = ExecStatus.FAILURE;
             }
-            if (execStatus != PropagationTaskExecStatus.SUCCESS) {
+            if (execStatus != ExecStatus.SUCCESS) {
                 throw new PropagationException(task.getResource(), execution 
== null ? null : execution.getMessage());
             }
         });
@@ -128,8 +128,7 @@ public class PriorityPropagationTaskExecutor extends 
AbstractPropagationTaskExec
         if (!nullPriority.isEmpty()) {
             if (nullPriorityAsync) {
                 nullPriority.forEach((task, exec) -> {
-                    reporter.onSuccessOrNonPriorityResourceFailures(
-                            task, PropagationTaskExecStatus.CREATED, null, 
null, null);
+                    reporter.onSuccessOrNonPriorityResourceFailures(task, 
ExecStatus.CREATED, null, null, null);
                 });
             } else {
                 final Set<Future<TaskExec>> nullPriorityFutures = new 
HashSet<>(nullPriority.values());

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
----------------------------------------------------------------------
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
index 06f6da9..4d42271 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
@@ -31,7 +31,7 @@ import org.apache.syncope.common.lib.types.AuditElements;
 import org.apache.syncope.common.lib.types.AuditElements.Result;
 import org.apache.syncope.common.lib.types.MatchingRule;
 import org.apache.syncope.common.lib.types.PatchOperation;
-import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
+import org.apache.syncope.common.lib.types.ExecStatus;
 import org.apache.syncope.core.provisioning.api.PropagationByResource;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.common.lib.types.UnmatchingRule;
@@ -485,7 +485,7 @@ public abstract class AbstractPushResultHandler extends 
AbstractSyncopeResultHan
         }
     }
 
-    protected ProvisioningReport.Status toProvisioningReportStatus(final 
PropagationTaskExecStatus status) {
+    protected ProvisioningReport.Status toProvisioningReportStatus(final 
ExecStatus status) {
         switch (status) {
             case FAILURE:
                 return ProvisioningReport.Status.FAILURE;

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java
----------------------------------------------------------------------
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java
index 2a4a510..4973407 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java
@@ -29,7 +29,7 @@ import org.apache.syncope.common.lib.to.RealmTO;
 import org.apache.syncope.common.lib.types.AuditElements;
 import org.apache.syncope.common.lib.types.AuditElements.Result;
 import org.apache.syncope.common.lib.types.MatchingRule;
-import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
+import org.apache.syncope.common.lib.types.ExecStatus;
 import org.apache.syncope.core.provisioning.api.PropagationByResource;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.common.lib.types.UnmatchingRule;
@@ -444,7 +444,7 @@ public class DefaultRealmPushResultHandler
         }
     }
 
-    private ProvisioningReport.Status toProvisioningReportStatus(final 
PropagationTaskExecStatus status) {
+    private ProvisioningReport.Status toProvisioningReportStatus(final 
ExecStatus status) {
         switch (status) {
             case FAILURE:
                 return ProvisioningReport.Status.FAILURE;

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java
----------------------------------------------------------------------
diff --git 
a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java
 
b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java
index 9d028e4..59a451a 100644
--- 
a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java
+++ 
b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java
@@ -18,10 +18,12 @@
  */
 package org.apache.syncope.core.rest.cxf.service;
 
+import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
+import java.util.stream.Collectors;
 import javax.ws.rs.BadRequestException;
 import javax.ws.rs.core.Response;
 import org.apache.commons.lang3.StringUtils;
@@ -30,29 +32,27 @@ import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.patch.AnyPatch;
 import org.apache.syncope.common.lib.patch.AssociationPatch;
 import org.apache.syncope.common.lib.patch.AttrPatch;
-import org.apache.syncope.common.lib.patch.BooleanReplacePatchItem;
 import org.apache.syncope.common.lib.patch.DeassociationPatch;
-import org.apache.syncope.common.lib.patch.StatusPatch;
-import org.apache.syncope.common.lib.patch.UserPatch;
 import org.apache.syncope.common.lib.search.SpecialAttr;
 import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.common.lib.to.AttrTO;
-import org.apache.syncope.common.lib.to.BulkAction;
-import org.apache.syncope.common.lib.to.BulkActionResult;
 import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.lib.to.ProvisioningResult;
 import org.apache.syncope.common.lib.types.PatchOperation;
 import org.apache.syncope.common.lib.types.ResourceAssociationAction;
 import org.apache.syncope.common.lib.types.ResourceDeassociationAction;
 import org.apache.syncope.common.lib.types.SchemaType;
-import org.apache.syncope.common.lib.types.StatusPatchType;
+import org.apache.syncope.common.rest.api.Preference;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.batch.BatchPayloadGenerator;
+import org.apache.syncope.common.rest.api.batch.BatchResponseItem;
 import org.apache.syncope.common.rest.api.beans.AnyQuery;
 import org.apache.syncope.common.rest.api.service.AnyService;
 import org.apache.syncope.core.logic.AbstractAnyLogic;
-import org.apache.syncope.core.logic.UserLogic;
 import org.apache.syncope.core.persistence.api.dao.AnyDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 
 public abstract class AbstractAnyService<TO extends AnyTO, P extends AnyPatch>
         extends AbstractServiceImpl
@@ -230,28 +230,60 @@ public abstract class AbstractAnyService<TO extends 
AnyTO, P extends AnyPatch>
                 break;
 
             default:
-                updated = new ProvisioningResult<>();
-                updated.setEntity(getAnyLogic().read(patch.getKey()));
+                throw new BadRequestException("Missing action");
         }
 
-        BulkActionResult result = new BulkActionResult();
-
+        List<BatchResponseItem> batchResponseItems;
         if (patch.getAction() == ResourceDeassociationAction.UNLINK) {
-            patch.getResources().forEach(resource -> {
-                result.getResults().put(
-                        resource,
-                        updated.getEntity().getResources().contains(resource)
-                        ? BulkActionResult.Status.FAILURE
-                        : BulkActionResult.Status.SUCCESS);
-            });
+            batchResponseItems = patch.getResources().stream().map(resource -> 
{
+                BatchResponseItem item = new BatchResponseItem();
+
+                item.getHeaders().put(RESTHeaders.RESOURCE_KEY, 
Arrays.asList(resource));
+
+                
item.setStatus(updated.getEntity().getResources().contains(resource)
+                        ? Response.Status.BAD_REQUEST.getStatusCode()
+                        : Response.Status.OK.getStatusCode());
+
+                if (getPreference() == Preference.RETURN_NO_CONTENT) {
+                    item.getHeaders().put(
+                            RESTHeaders.PREFERENCE_APPLIED,
+                            
Arrays.asList(Preference.RETURN_NO_CONTENT.toString()));
+                } else {
+                    item.setContent(POJOHelper.serialize(updated.getEntity()));
+                }
+
+                return item;
+            }).collect(Collectors.toList());
         } else {
-            updated.getPropagationStatuses().forEach(propagationStatusTO
-                    -> result.getResults().put(
-                            propagationStatusTO.getResource(),
-                            
BulkActionResult.Status.valueOf(propagationStatusTO.getStatus().toString())));
+            batchResponseItems = updated.getPropagationStatuses().stream().
+                    map(status -> {
+                        BatchResponseItem item = new BatchResponseItem();
+
+                        item.getHeaders().put(RESTHeaders.RESOURCE_KEY, 
Arrays.asList(status.getResource()));
+
+                        item.setStatus(status.getStatus().getHttpStatus());
+
+                        if (status.getFailureReason() != null) {
+                            item.getHeaders().put(RESTHeaders.ERROR_INFO, 
Arrays.asList(status.getFailureReason()));
+                        }
+
+                        if (getPreference() == Preference.RETURN_NO_CONTENT) {
+                            item.getHeaders().put(
+                                    RESTHeaders.PREFERENCE_APPLIED,
+                                    
Arrays.asList(Preference.RETURN_NO_CONTENT.toString()));
+                        } else {
+                            
item.setContent(POJOHelper.serialize(updated.getEntity()));
+                        }
+
+                        return item;
+                    }).collect(Collectors.toList());
         }
 
-        return modificationResponse(result);
+        String boundary = "deassociate_" + GENERATOR.generate().toString();
+        return Response.ok(BatchPayloadGenerator.generate(
+                batchResponseItems, SyncopeConstants.DOUBLE_DASH + boundary)).
+                type(RESTHeaders.multipartMixedWith(boundary)).
+                build();
     }
 
     @Override
@@ -287,121 +319,59 @@ public abstract class AbstractAnyService<TO extends 
AnyTO, P extends AnyPatch>
                 break;
 
             default:
-                updated = new ProvisioningResult<>();
-                updated.setEntity(getAnyLogic().read(patch.getKey()));
+                throw new BadRequestException("Missing action");
         }
 
-        BulkActionResult result = new BulkActionResult();
-
+        List<BatchResponseItem> batchResponseItems;
         if (patch.getAction() == ResourceAssociationAction.LINK) {
-            patch.getResources().forEach(resource -> {
-                result.getResults().put(
-                        resource,
-                        updated.getEntity().getResources().contains(resource)
-                        ? BulkActionResult.Status.SUCCESS
-                        : BulkActionResult.Status.FAILURE);
-            });
-        } else {
-            updated.getPropagationStatuses().forEach(propagationStatusTO
-                    -> result.getResults().put(
-                            propagationStatusTO.getResource(),
-                            
BulkActionResult.Status.valueOf(propagationStatusTO.getStatus().toString())));
-        }
+            batchResponseItems = patch.getResources().stream().map(resource -> 
{
+                BatchResponseItem item = new BatchResponseItem();
 
-        return modificationResponse(result);
-    }
+                item.getHeaders().put(RESTHeaders.RESOURCE_KEY, 
Arrays.asList(resource));
 
-    @Override
-    public Response bulk(final BulkAction bulkAction) {
-        AbstractAnyLogic<TO, P> logic = getAnyLogic();
-
-        BulkActionResult result = new BulkActionResult();
-
-        switch (bulkAction.getType()) {
-            case MUSTCHANGEPASSWORD:
-                if (logic instanceof UserLogic) {
-                    bulkAction.getTargets().forEach(key -> {
-                        try {
-                            final UserPatch userPatch = new UserPatch();
-                            userPatch.setKey(key);
-                            userPatch.setMustChangePassword(new 
BooleanReplacePatchItem.Builder().value(true).build());
-
-                            result.getResults().put(
-                                    ((UserLogic) logic).update(userPatch, 
false).getEntity().getKey(),
-                                    BulkActionResult.Status.SUCCESS);
-                        } catch (Exception e) {
-                            LOG.error("Error performing delete for user {}", 
key, e);
-                            result.getResults().put(key, 
BulkActionResult.Status.FAILURE);
-                        }
-                    });
+                
item.setStatus(updated.getEntity().getResources().contains(resource)
+                        ? Response.Status.OK.getStatusCode()
+                        : Response.Status.BAD_REQUEST.getStatusCode());
+
+                if (getPreference() == Preference.RETURN_NO_CONTENT) {
+                    item.getHeaders().put(
+                            RESTHeaders.PREFERENCE_APPLIED,
+                            
Arrays.asList(Preference.RETURN_NO_CONTENT.toString()));
                 } else {
-                    throw new BadRequestException();
+                    item.setContent(POJOHelper.serialize(updated.getEntity()));
                 }
-                break;
 
-            case DELETE:
-                bulkAction.getTargets().forEach(key -> {
-                    try {
-                        result.getResults().put(
-                                logic.delete(key, 
isNullPriorityAsync()).getEntity().getKey(),
-                                BulkActionResult.Status.SUCCESS);
-                    } catch (Exception e) {
-                        LOG.error("Error performing delete for user {}", key, 
e);
-                        result.getResults().put(key, 
BulkActionResult.Status.FAILURE);
-                    }
-                });
-                break;
+                return item;
+            }).collect(Collectors.toList());
+        } else {
+            batchResponseItems = updated.getPropagationStatuses().stream().
+                    map(status -> {
+                        BatchResponseItem item = new BatchResponseItem();
+
+                        item.getHeaders().put(RESTHeaders.RESOURCE_KEY, 
Arrays.asList(status.getResource()));
 
-            case SUSPEND:
-                if (logic instanceof UserLogic) {
-                    bulkAction.getTargets().forEach(key -> {
-                        StatusPatch statusPatch = new 
StatusPatch.Builder().key(key).
-                                type(StatusPatchType.SUSPEND).
-                                onSyncope(true).
-                                build();
-
-                        try {
-                            result.getResults().put(
-                                    ((UserLogic) logic).
-                                            status(statusPatch, 
isNullPriorityAsync()).getEntity().getKey(),
-                                    BulkActionResult.Status.SUCCESS);
-                        } catch (Exception e) {
-                            LOG.error("Error performing suspend for user {}", 
key, e);
-                            result.getResults().put(key, 
BulkActionResult.Status.FAILURE);
+                        item.setStatus(status.getStatus().getHttpStatus());
+
+                        if (status.getFailureReason() != null) {
+                            item.getHeaders().put(RESTHeaders.ERROR_INFO, 
Arrays.asList(status.getFailureReason()));
                         }
-                    });
-                } else {
-                    throw new BadRequestException();
-                }
-                break;
 
-            case REACTIVATE:
-                if (logic instanceof UserLogic) {
-                    bulkAction.getTargets().forEach(key -> {
-                        StatusPatch statusPatch = new 
StatusPatch.Builder().key(key).
-                                type(StatusPatchType.REACTIVATE).
-                                onSyncope(true).
-                                build();
-
-                        try {
-                            result.getResults().put(
-                                    ((UserLogic) logic).
-                                            status(statusPatch, 
isNullPriorityAsync()).getEntity().getKey(),
-                                    BulkActionResult.Status.SUCCESS);
-                        } catch (Exception e) {
-                            LOG.error("Error performing reactivate for user 
{}", key, e);
-                            result.getResults().put(key, 
BulkActionResult.Status.FAILURE);
+                        if (getPreference() == Preference.RETURN_NO_CONTENT) {
+                            item.getHeaders().put(
+                                    RESTHeaders.PREFERENCE_APPLIED,
+                                    
Arrays.asList(Preference.RETURN_NO_CONTENT.toString()));
+                        } else {
+                            
item.setContent(POJOHelper.serialize(updated.getEntity()));
                         }
-                    });
-                } else {
-                    throw new BadRequestException();
-                }
-                break;
 
-            default:
+                        return item;
+                    }).collect(Collectors.toList());
         }
 
-        return modificationResponse(result);
+        String boundary = "associate_" + GENERATOR.generate().toString();
+        return Response.ok(BatchPayloadGenerator.generate(
+                batchResponseItems, SyncopeConstants.DOUBLE_DASH + boundary)).
+                type(RESTHeaders.multipartMixedWith(boundary)).
+                build();
     }
-
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/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
index 81ef47c..569da48 100644
--- 
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
@@ -19,13 +19,17 @@
 package org.apache.syncope.core.rest.cxf.service;
 
 import java.util.List;
+import javax.ws.rs.core.Response;
 import org.apache.commons.lang3.tuple.Pair;
-import org.apache.syncope.common.lib.to.BulkActionResult;
+import org.apache.syncope.common.lib.SyncopeConstants;
 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.RESTHeaders;
+import org.apache.syncope.common.rest.api.batch.BatchPayloadGenerator;
+import org.apache.syncope.common.rest.api.batch.BatchResponseItem;
+import org.apache.syncope.common.rest.api.beans.ExecDeleteQuery;
 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;
@@ -56,13 +60,19 @@ public abstract class AbstractExecutableService extends 
AbstractServiceImpl impl
     }
 
     @Override
-    public BulkActionResult deleteExecutions(final BulkExecDeleteQuery query) {
-        return getExecutableLogic().deleteExecutions(
+    public Response deleteExecutions(final ExecDeleteQuery query) {
+        List<BatchResponseItem> batchResponseItems = 
getExecutableLogic().deleteExecutions(
                 query.getKey(),
                 query.getStartedBefore(),
                 query.getStartedAfter(),
                 query.getEndedBefore(),
                 query.getEndedAfter());
+
+        String boundary = "deleteExecutions_" + 
GENERATOR.generate().toString();
+        return Response.ok(BatchPayloadGenerator.generate(
+                batchResponseItems, SyncopeConstants.DOUBLE_DASH + boundary)).
+                type(RESTHeaders.multipartMixedWith(boundary)).
+                build();
     }
 
     @Override
@@ -84,5 +94,4 @@ public abstract class AbstractExecutableService extends 
AbstractServiceImpl impl
     public void actionJob(final String key, final JobAction action) {
         getExecutableLogic().actionJob(key, action);
     }
-
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java
----------------------------------------------------------------------
diff --git 
a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java
 
b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java
index 2e9a0c0..c92e7a2 100644
--- 
a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java
+++ 
b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.core.rest.cxf.service;
 
+import com.fasterxml.uuid.Generators;
+import com.fasterxml.uuid.impl.RandomBasedGenerator;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -56,6 +58,8 @@ abstract class AbstractServiceImpl implements JAXRSService {
 
     protected static final Logger LOG = 
LoggerFactory.getLogger(AbstractServiceImpl.class);
 
+    protected static final RandomBasedGenerator GENERATOR = 
Generators.randomBasedGenerator();
+
     protected static final String OPTIONS_ALLOW = "GET,POST,OPTIONS,HEAD";
 
     @Context

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/GroupServiceImpl.java
----------------------------------------------------------------------
diff --git 
a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/GroupServiceImpl.java
 
b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/GroupServiceImpl.java
index bc621e9..add4b98 100644
--- 
a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/GroupServiceImpl.java
+++ 
b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/GroupServiceImpl.java
@@ -25,7 +25,7 @@ import org.apache.syncope.common.lib.patch.GroupPatch;
 import org.apache.syncope.common.lib.to.ExecTO;
 import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.common.lib.to.ProvisioningResult;
-import org.apache.syncope.common.lib.types.BulkMembersActionType;
+import org.apache.syncope.common.lib.types.ProvisionAction;
 import org.apache.syncope.common.rest.api.service.GroupService;
 import org.apache.syncope.core.logic.AbstractAnyLogic;
 import org.apache.syncope.core.logic.GroupLogic;
@@ -89,8 +89,7 @@ public class GroupServiceImpl extends 
AbstractAnyService<GroupTO, GroupPatch> im
     }
 
     @Override
-    public ExecTO bulkMembersAction(final String key, final 
BulkMembersActionType actionType) {
-        return logic.bulkMembersAction(key, actionType);
+    public ExecTO provisionMembers(final String key, final ProvisionAction 
action) {
+        return logic.provisionMembers(key, action);
     }
-
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ResourceServiceImpl.java
----------------------------------------------------------------------
diff --git 
a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ResourceServiceImpl.java
 
b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ResourceServiceImpl.java
index 2b76dfc..70ac9ee 100644
--- 
a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ResourceServiceImpl.java
+++ 
b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ResourceServiceImpl.java
@@ -19,26 +19,19 @@
 package org.apache.syncope.core.rest.cxf.service;
 
 import java.net.URI;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.Pair;
-import org.apache.syncope.common.lib.patch.ResourceDeassociationPatch;
-import org.apache.syncope.common.lib.to.AnyTO;
-import org.apache.syncope.common.lib.to.BulkActionResult;
 import org.apache.syncope.common.lib.to.ConnObjectTO;
 import org.apache.syncope.common.lib.to.PagedConnObjectTOResult;
 import org.apache.syncope.common.lib.to.ResourceTO;
-import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.rest.api.RESTHeaders;
 import org.apache.syncope.common.rest.api.beans.ConnObjectTOListQuery;
 import org.apache.syncope.common.rest.api.service.ResourceService;
-import org.apache.syncope.core.logic.AbstractResourceAssociator;
 import org.apache.syncope.core.logic.AnyObjectLogic;
 import org.apache.syncope.core.logic.ResourceLogic;
 import org.apache.syncope.core.logic.GroupLogic;
@@ -141,44 +134,4 @@ public class ResourceServiceImpl extends 
AbstractServiceImpl implements Resource
     public void check(final ResourceTO resourceTO) {
         logic.check(resourceTO);
     }
-
-    @Override
-    public BulkActionResult bulkDeassociation(final ResourceDeassociationPatch 
patch) {
-        AbstractResourceAssociator<? extends AnyTO> associator =
-                patch.getAnyTypeKey().equalsIgnoreCase(AnyTypeKind.USER.name())
-                ? userLogic
-                : 
patch.getAnyTypeKey().equalsIgnoreCase(AnyTypeKind.GROUP.name())
-                ? groupLogic
-                : anyObjectLogic;
-
-        BulkActionResult result = new BulkActionResult();
-
-        for (String anyKey : patch.getAnyKyes()) {
-            Set<String> resources = Collections.singleton(patch.getKey());
-            try {
-                switch (patch.getAction()) {
-                    case DEPROVISION:
-                        associator.deprovision(anyKey, resources, 
isNullPriorityAsync());
-                        break;
-
-                    case UNASSIGN:
-                        associator.unassign(anyKey, resources, 
isNullPriorityAsync());
-                        break;
-
-                    case UNLINK:
-                        associator.unlink(anyKey, resources);
-                        break;
-
-                    default:
-                }
-
-                result.getResults().put(anyKey, 
BulkActionResult.Status.SUCCESS);
-            } catch (Exception e) {
-                LOG.warn("While executing {} on {} {}", patch.getAction(), 
patch.getAnyTypeKey(), anyKey, e);
-                result.getResults().put(anyKey, 
BulkActionResult.Status.FAILURE);
-            }
-        }
-
-        return result;
-    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/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 cbeb886..cd48508 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
@@ -24,8 +24,6 @@ import javax.ws.rs.BadRequestException;
 import javax.ws.rs.core.Response;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.to.TaskTO;
-import org.apache.syncope.common.lib.to.BulkAction;
-import org.apache.syncope.common.lib.to.BulkActionResult;
 import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.lib.to.SchedTaskTO;
 import org.apache.syncope.common.lib.types.TaskType;
@@ -93,50 +91,4 @@ public class TaskServiceImpl extends 
AbstractExecutableService implements TaskSe
     public void update(final TaskType type, final SchedTaskTO taskTO) {
         logic.updateSchedTask(type, taskTO);
     }
-
-    @Override
-    public BulkActionResult bulk(final BulkAction bulkAction) {
-        BulkActionResult result = new BulkActionResult();
-
-        switch (bulkAction.getType()) {
-            case DELETE:
-                for (String key : bulkAction.getTargets()) {
-                    try {
-                        result.getResults().put(logic.delete(null, 
key).getKey(), BulkActionResult.Status.SUCCESS);
-                    } catch (Exception e) {
-                        LOG.error("Error performing delete for task {}", key, 
e);
-                        result.getResults().put(key, 
BulkActionResult.Status.FAILURE);
-                    }
-                }
-                break;
-
-            case DRYRUN:
-                for (String key : bulkAction.getTargets()) {
-                    try {
-                        logic.execute(key, null, true);
-                        result.getResults().put(key, 
BulkActionResult.Status.SUCCESS);
-                    } catch (Exception e) {
-                        LOG.error("Error performing dryrun for task {}", key, 
e);
-                        result.getResults().put(key, 
BulkActionResult.Status.FAILURE);
-                    }
-                }
-                break;
-
-            case EXECUTE:
-                for (String key : bulkAction.getTargets()) {
-                    try {
-                        logic.execute(key, null, false);
-                        result.getResults().put(key, 
BulkActionResult.Status.SUCCESS);
-                    } catch (Exception e) {
-                        LOG.error("Error performing execute for task {}", key, 
e);
-                        result.getResults().put(key, 
BulkActionResult.Status.FAILURE);
-                    }
-                }
-                break;
-
-            default:
-        }
-
-        return result;
-    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/ext/camel/client-console/src/main/java/org/apache/syncope/client/console/panels/CamelRoutesDirectoryPanel.java
----------------------------------------------------------------------
diff --git 
a/ext/camel/client-console/src/main/java/org/apache/syncope/client/console/panels/CamelRoutesDirectoryPanel.java
 
b/ext/camel/client-console/src/main/java/org/apache/syncope/client/console/panels/CamelRoutesDirectoryPanel.java
index cddb413..00307c8 100644
--- 
a/ext/camel/client-console/src/main/java/org/apache/syncope/client/console/panels/CamelRoutesDirectoryPanel.java
+++ 
b/ext/camel/client-console/src/main/java/org/apache/syncope/client/console/panels/CamelRoutesDirectoryPanel.java
@@ -93,7 +93,7 @@ public class CamelRoutesDirectoryPanel extends DirectoryPanel<
     }
 
     @Override
-    protected Collection<ActionLink.ActionType> getBulkActions() {
+    protected Collection<ActionLink.ActionType> getBatches() {
         return Collections.<ActionLink.ActionType>emptyList();
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.java
----------------------------------------------------------------------
diff --git 
a/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.java
 
b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.java
index dd66cc6..e3f449e 100644
--- 
a/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.java
+++ 
b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.java
@@ -159,7 +159,7 @@ public class OIDCProvidersDirectoryPanel extends 
DirectoryPanel<
     }
 
     @Override
-    protected Collection<ActionLink.ActionType> getBulkActions() {
+    protected Collection<ActionLink.ActionType> getBatches() {
         return Collections.<ActionLink.ActionType>emptyList();
 
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/ext/saml2sp/client-console/src/main/java/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel.java
----------------------------------------------------------------------
diff --git 
a/ext/saml2sp/client-console/src/main/java/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel.java
 
b/ext/saml2sp/client-console/src/main/java/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel.java
index e4dd9fd..2e602e5 100644
--- 
a/ext/saml2sp/client-console/src/main/java/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel.java
+++ 
b/ext/saml2sp/client-console/src/main/java/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel.java
@@ -177,7 +177,7 @@ public class SAML2IdPsDirectoryPanel extends DirectoryPanel<
     }
 
     @Override
-    protected Collection<ActionLink.ActionType> getBulkActions() {
+    protected Collection<ActionLink.ActionType> getBatches() {
         return Collections.<ActionLink.ActionType>emptyList();
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a2fdbb/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
----------------------------------------------------------------------
diff --git 
a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java 
b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index 4ce392d..151f426 100644
--- 
a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++ 
b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -22,6 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.fail;
 
+import java.io.IOException;
 import java.io.InputStream;
 import java.net.URI;
 import java.util.ArrayList;
@@ -69,6 +70,8 @@ import org.apache.syncope.common.lib.types.PolicyType;
 import org.apache.syncope.common.lib.types.SchemaType;
 import org.apache.syncope.common.lib.types.TraceLevel;
 import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.batch.BatchPayloadParser;
+import org.apache.syncope.common.rest.api.batch.BatchResponseItem;
 import org.apache.syncope.common.rest.api.service.AnyObjectService;
 import org.apache.syncope.common.rest.api.service.AnyTypeClassService;
 import org.apache.syncope.common.rest.api.service.AnyTypeService;
@@ -536,6 +539,12 @@ public abstract class AbstractITCase {
         return getObject(response.getLocation(), ResourceService.class, 
ResourceTO.class);
     }
 
+    protected List<BatchResponseItem> parseBatchResponse(final Response 
response) throws IOException {
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+        return BatchPayloadParser.parse(
+                (InputStream) response.getEntity(), response.getMediaType(), 
new BatchResponseItem());
+    }
+
     @SuppressWarnings({ "unchecked", "rawtypes", "UseOfObsoleteCollectionType" 
})
     protected InitialDirContext getLdapResourceDirContext(final String bindDn, 
final String bindPwd)
             throws NamingException {

Reply via email to