Task: one table per class + Preferring JSON arrays to @ElementCollection (#374)

diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/ConnectorDataProvider.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/ConnectorDataProvider.java
index dc21b11..8d7f7cb 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/ConnectorDataProvider.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/ConnectorDataProvider.java
@@ -51,15 +51,15 @@
 
     protected int currentPage;
 
-    private String keyword;
-
-    private final ConnectorRestClient restClient = new ConnectorRestClient();
+    private final String keyword;
 
     public ConnectorDataProvider(
             final int paginatorRows,
             final PageReference pageRef,
             final String keyword) {
+
         super(paginatorRows);
+
         setSort("displayNameSortParam", SortOrder.ASCENDING);
         this.pageRef = pageRef;
         this.keyword = keyword;
@@ -75,18 +75,18 @@
                 currentPage = 0;
             }
             if (StringUtils.isBlank(keyword)) {
-                result = restClient.getAllConnectors();
+                result = ConnectorRestClient.getAllConnectors();
             } else {
-                result = restClient.getAllConnectors().stream().filter(conn ->
-                        conn.getDisplayName().toLowerCase().contains(keyword)).collect(Collectors.toList());
+                result = ConnectorRestClient.getAllConnectors().stream().
+                        filter(conn -> conn.getDisplayName().toLowerCase().contains(keyword)).
+                        collect(Collectors.toList());
             }
         } catch (Exception e) {
             LOG.error("While searching", e);
             SyncopeConsoleSession.get().onException(e);
 
             Optional<AjaxRequestTarget> target = RequestCycle.get().find(AjaxRequestTarget.class);
-            target.ifPresent(ajaxRequestTarget ->
-                    ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(ajaxRequestTarget));
+            target.ifPresent(t -> ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(t));
         }
 
         SortParam<String> sortParam = getSort();
@@ -126,10 +126,10 @@
 
         try {
             if (StringUtils.isBlank(keyword)) {
-                result = restClient.getAllConnectors().size();
+                result = ConnectorRestClient.getAllConnectors().size();
             } else {
-                result = restClient.getAllConnectors().stream().filter(conn ->
-                        conn.getDisplayName().toLowerCase().contains(keyword)).count();
+                result = ConnectorRestClient.getAllConnectors().stream().filter(conn
+                        -> conn.getDisplayName().toLowerCase().contains(keyword)).count();
             }
         } catch (Exception e) {
             LOG.error("While requesting for size()", e);
@@ -144,7 +144,7 @@
 
     @Override
     public IModel<Serializable> model(final Serializable object) {
-        return new CompoundPropertyModel<>((ConnInstanceTO) object);
+        return new CompoundPropertyModel<>(object);
     }
 
     public int getCurrentPage() {
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/ResourceDataProvider.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/ResourceDataProvider.java
index 2b78445..b19c8c1 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/ResourceDataProvider.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/ResourceDataProvider.java
@@ -51,15 +51,15 @@
 
     protected int currentPage;
 
-    private String keyword;
-
-    private final ResourceRestClient restClient = new ResourceRestClient();
+    private final String keyword;
 
     public ResourceDataProvider(
             final int paginatorRows,
             final PageReference pageRef,
             final String keyword) {
+
         super(paginatorRows);
+
         setSort("keySortParam", SortOrder.ASCENDING);
         this.pageRef = pageRef;
         this.keyword = keyword;
@@ -75,18 +75,19 @@
                 currentPage = 0;
             }
             if (StringUtils.isBlank(keyword)) {
-                result = restClient.list();
+                result = ResourceRestClient.list();
             } else {
-                result = restClient.list().stream().filter(resource ->
-                        resource.getKey().toLowerCase().contains(keyword)).collect(Collectors.toList());
+                result = ResourceRestClient.list().stream().
+                        filter(resource -> resource.getKey().toLowerCase().contains(keyword)).
+                        collect(Collectors.toList());
             }
         } catch (Exception e) {
             LOG.error("While searching", e);
             SyncopeConsoleSession.get().onException(e);
 
             Optional<AjaxRequestTarget> target = RequestCycle.get().find(AjaxRequestTarget.class);
-            target.ifPresent(ajaxRequestTarget ->
-                    ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(ajaxRequestTarget));
+            target.ifPresent(ajaxRequestTarget
+                    -> ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(ajaxRequestTarget));
         }
 
         SortParam<String> sortParam = getSort();
@@ -126,10 +127,10 @@
 
         try {
             if (StringUtils.isBlank(keyword)) {
-                result = restClient.list().size();
+                result = ResourceRestClient.list().size();
             } else {
-                result = restClient.list().stream().filter(resource ->
-                        resource.getKey().toLowerCase().contains(keyword)).count();
+                result = ResourceRestClient.list().stream().filter(resource
+                        -> resource.getKey().toLowerCase().contains(keyword)).count();
             }
         } catch (Exception e) {
             LOG.error("While requesting for size()", e);
@@ -144,7 +145,7 @@
 
     @Override
     public IModel<Serializable> model(final Serializable object) {
-        return new CompoundPropertyModel<>((ResourceTO) object);
+        return new CompoundPropertyModel<>(object);
     }
 
     public int getCurrentPage() {
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjectListViewPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjectListViewPanel.java
index a4d24cb..529dd80 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjectListViewPanel.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjectListViewPanel.java
@@ -307,7 +307,7 @@
             final String cookie,
             final String fiql) {
 
-        Pair<String, List<ConnObject>> items = new ResourceRestClient().searchConnObjects(
+        Pair<String, List<ConnObject>> items = ResourceRestClient.searchConnObjects(
                 resource,
                 anyType,
                 new ConnObjectTOQuery.Builder().
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/search/ConnObjectSearchPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/search/ConnObjectSearchPanel.java
index 4645e47..0ae25e6 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/search/ConnObjectSearchPanel.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/search/ConnObjectSearchPanel.java
@@ -37,8 +37,6 @@
 
     private static final long serialVersionUID = 21020550706646L;
 
-    protected final ConnectorRestClient connectorRestClient = new ConnectorRestClient();
-
     protected final ResourceTO resource;
 
     public static class Builder extends AbstractSearchPanel.Builder<ConnObjectSearchPanel> {
@@ -118,11 +116,12 @@
 
             @Override
             protected Map<String, PlainSchemaTO> load() {
-                return connectorRestClient.buildObjectClassInfo(
-                        connectorRestClient.read(resource.getConnector()), false).stream().
+                return ConnectorRestClient.buildObjectClassInfo(
+                        ConnectorRestClient.read(resource.getConnector()), false).stream().
                         map(ConnIdObjectClass::getAttributes).
                         flatMap(List::stream).
-                        collect(Collectors.toMap(PlainSchemaTO::getKey, Function.identity(),
+                        collect(Collectors.toMap(
+                                PlainSchemaTO::getKey, Function.identity(),
                                 (schema1, schema2) -> schema1));
             }
         };
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountPrivilegesPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountPrivilegesPanel.java
index d9898b4..7acd107 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountPrivilegesPanel.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountPrivilegesPanel.java
@@ -33,8 +33,6 @@
 
     private static final long serialVersionUID = 3388483585148725922L;
 
-    private final ApplicationRestClient applicationRestClient = new ApplicationRestClient();
-
     public LinkedAccountPrivilegesPanel(final LinkedAccountTO linkedAccountTO) {
         super();
         setOutputMarkupId(true);
@@ -45,7 +43,7 @@
 
             @Override
             protected List<String> load() {
-                return applicationRestClient.list().stream().
+                return ApplicationRestClient.list().stream().
                     flatMap(app -> app.getPrivileges().stream()).
                     map(PrivilegeTO::getKey).
                     distinct().
@@ -63,5 +61,4 @@
         privilegesPanel.setOutputMarkupId(true);
         add(privilegesPanel);
     }
-
 }
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/MergeLinkedAccountsSearchPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/MergeLinkedAccountsSearchPanel.java
index 19d8ac2..e73e5c9 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/MergeLinkedAccountsSearchPanel.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/MergeLinkedAccountsSearchPanel.java
@@ -50,10 +50,6 @@
 
     private final UserSearchPanel userSearchPanel;
 
-    private final AnyTypeClassRestClient anyTypeClassRestClient = new AnyTypeClassRestClient();
-
-    private final AnyTypeRestClient anyTypeRestClient = new AnyTypeRestClient();
-
     private final UserSelectionDirectoryPanel userDirectoryPanel;
 
     private final Fragment userSearchFragment;
@@ -77,9 +73,9 @@
                 build("usersearch"));
         userSearchFragment.add(userSearchPanel);
 
-        AnyTypeTO anyTypeTO = anyTypeRestClient.read(AnyTypeKind.USER.name());
+        AnyTypeTO anyTypeTO = AnyTypeRestClient.read(AnyTypeKind.USER.name());
         userDirectoryPanel = UserSelectionDirectoryPanel.class.cast(new UserSelectionDirectoryPanel.Builder(
-                anyTypeClassRestClient.list(anyTypeTO.getClasses()), anyTypeTO.getKey(), pageRef).
+                AnyTypeClassRestClient.list(anyTypeTO.getClasses()), anyTypeTO.getKey(), pageRef).
                 build("searchResult"));
 
         userSearchFragment.add(userDirectoryPanel);
diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AjaxGrid.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AjaxGrid.java
index 2190f80..9c0f94a 100644
--- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AjaxGrid.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AjaxGrid.java
@@ -222,6 +222,8 @@
 
     protected class AjaxGridActionsPanel<T> extends EditableGridActionsPanel<Pair<K, V>> {
 
+        private static final long serialVersionUID = -1239486389000098745L;
+
         private final Item<Pair<K, V>> rowItem;
 
         @SuppressWarnings("unchecked")
@@ -231,10 +233,12 @@
             rowItem = item.findParent(Item.class);
             addOrReplace(new AjaxLink<String>("delete") {
 
+                private static final long serialVersionUID = 1049203640150071039L;
+
                 @SuppressWarnings("unchecked")
                 @Override
                 public void onClick(final AjaxRequestTarget target) {
-                    EditableDataTable eventTarget = rowItem.findParent(EditableDataTable.class);
+                    EditableDataTable<?, ?> eventTarget = rowItem.findParent(EditableDataTable.class);
                     send(getPage(), Broadcast.BREADTH, new GridOperationData<>(
                             OperationType.DELETE, (T) rowItem.getDefaultModelObject(), eventTarget));
                     target.add(eventTarget);
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/layout/AnyLayoutUtils.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/layout/AnyLayoutUtils.java
index 4b3e01b..05c8973 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/layout/AnyLayoutUtils.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/layout/AnyLayoutUtils.java
@@ -41,8 +41,6 @@
 
 public final class AnyLayoutUtils {
 
-    private static final RoleRestClient ROLE_REST_CLIENT = new RoleRestClient();
-
     private static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build();
 
     private static void setUserIfEmpty(final AnyLayout anyLayout) {
@@ -81,7 +79,7 @@
         try {
             AnyLayout anyLayout = null;
             for (int i = 0; i < ownedRoles.size() && anyLayout == null; i++) {
-                String anyLayoutJSON = ROLE_REST_CLIENT.readAnyLayout(ownedRoles.get(i));
+                String anyLayoutJSON = RoleRestClient.readAnyLayout(ownedRoles.get(i));
                 if (StringUtils.isNotBlank(anyLayoutJSON)) {
                     anyLayout = MAPPER.readValue(anyLayoutJSON, AnyLayout.class);
                 }
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/GroupDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/GroupDirectoryPanel.java
index fb1c93a..a2fe99d 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/GroupDirectoryPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/GroupDirectoryPanel.java
@@ -103,13 +103,10 @@
             protected Serializable onApplyInternal(
                     final GroupTO groupTO, final String type, final AjaxRequestTarget target) {
 
-                AnyTypeRestClient anyTypeRestClient = new AnyTypeRestClient();
-                AnyTypeClassRestClient classRestClient = new AnyTypeClassRestClient();
-
-                AnyLayout layout = AnyLayoutUtils.fetch(anyTypeRestClient.list());
+                AnyLayout layout = AnyLayoutUtils.fetch(AnyTypeRestClient.list());
                 ModalPanel anyPanel = AnyLayoutUtils.newAnyPanel(
                         layout.getAnyPanelClass(),
-                        BaseModal.CONTENT_ID, anyTypeRestClient.read(type), null, layout, false,
+                        BaseModal.CONTENT_ID, AnyTypeRestClient.read(type), null, layout, false,
                         (id, anyTypeTO, realmTO, anyLayout, pageRef) -> {
                             final Panel panel;
                             if (AnyTypeKind.USER.name().equals(type)) {
@@ -119,7 +116,8 @@
                                                 is(Constants.KEY_FIELD_NAME).notNullValue()).query();
 
                                 panel = new UserDirectoryPanel.Builder(
-                                        classRestClient.list(anyTypeTO.getClasses()), anyTypeTO.getKey(), pageRef).
+                                        AnyTypeClassRestClient.list(
+                                                anyTypeTO.getClasses()), anyTypeTO.getKey(), pageRef).
                                         setRealm(realm).
                                         setFiltered(true).
                                         setFiql(query).
@@ -141,7 +139,8 @@
                                                 is(Constants.KEY_FIELD_NAME).notNullValue()).query();
 
                                 panel = new AnyObjectDirectoryPanel.Builder(
-                                        classRestClient.list(anyTypeTO.getClasses()), anyTypeTO.getKey(), pageRef).
+                                        AnyTypeClassRestClient.list(
+                                                anyTypeTO.getClasses()), anyTypeTO.getKey(), pageRef).
                                         setRealm(realm).
                                         setFiltered(true).
                                         setFiql(query).
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel.java
index 9a120c0..3aa524b 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel.java
@@ -261,7 +261,7 @@
 
                 Component panel;
                 try {
-                    JobTO jobTO = restClient.getJob(rowModel.getObject().getKey());
+                    JobTO jobTO = TaskRestClient.getJob(rowModel.getObject().getKey());
                     panel = new JobActionPanel(componentId, jobTO, false, SchedTaskDirectoryPanel.this);
                     MetaDataRoleAuthorizationStrategy.authorize(
                         panel, WebPage.ENABLE,
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/CollectionPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/CollectionPanel.java
index f04fa5a..2492e96 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/CollectionPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/CollectionPanel.java
@@ -29,7 +29,7 @@
 
     private static final long serialVersionUID = -4042497356836230377L;
 
-    @SuppressWarnings("unchecked")
+    @SuppressWarnings({ "unchecked", "rawtypes" })
     public CollectionPanel(final String id, final List values) {
         super(id);
 
@@ -39,7 +39,7 @@
 
             @Override
             protected void populateItem(final ListItem item) {
-                final String value = item.getModelObject() == null ? null : item.getModelObject().toString();
+                String value = item.getModelObject() == null ? null : item.getModelObject().toString();
                 item.add(new Label("item", new ResourceModel(value, value)));
             }
         });
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/CollectionPropertyColumn.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/CollectionPropertyColumn.java
index eaac4dd..bad84d8 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/CollectionPropertyColumn.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/CollectionPropertyColumn.java
@@ -31,21 +31,19 @@
 
     private static final long serialVersionUID = 8077865338230121496L;
 
-    public CollectionPropertyColumn(
-            final IModel<String> displayModel,
-            final String propertyExpression) {
+    public CollectionPropertyColumn(final IModel<String> displayModel, final String propertyExpression) {
         super(displayModel, propertyExpression);
     }
 
+    @SuppressWarnings({ "unchecked", "rawtypes" })
     @Override
-    @SuppressWarnings("unchecked")
     public void populateItem(
             final Item<ICellPopulator<T>> cellItem, final String componentId, final IModel<T> rowModel) {
 
-        final Object value = getDataModel(rowModel).getObject();
+        Object value = getDataModel(rowModel).getObject();
 
         if (value instanceof Collection) {
-            final List values = new ArrayList((Collection) value);
+            List values = new ArrayList((Collection) value);
             Collections.sort(values);
             cellItem.add(new CollectionPanel(componentId, values));
         }
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
index c6b911d..91a1d49 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
@@ -154,7 +154,7 @@
 
     @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_UPDATE + "')")
     public <T extends SchedTaskTO> T updateSchedTask(final TaskType type, final SchedTaskTO taskTO) {
-        SchedTask task = taskDAO.find(taskTO.getKey());
+        SchedTask task = taskDAO.find(type, taskTO.getKey());
         if (task == null) {
             throw new NotFoundException("Task " + taskTO.getKey());
         }
@@ -224,7 +224,7 @@
     @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_READ + "')")
     @Transactional(readOnly = true)
     public <T extends TaskTO> T read(final TaskType type, final String key, final boolean details) {
-        Task task = taskDAO.find(key);
+        Task<?> task = taskDAO.find(type, key);
         if (task == null) {
             throw new NotFoundException("Task " + key);
         }
@@ -242,10 +242,7 @@
     @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_EXECUTE + "')")
     @Override
     public ExecTO execute(final String key, final OffsetDateTime startAt, final boolean dryRun) {
-        Task task = taskDAO.find(key);
-        if (task == null) {
-            throw new NotFoundException("Task " + key);
-        }
+        Task<?> task = taskDAO.find(key).orElseThrow(() -> new NotFoundException("Task " + key));
         if (startAt != null && startAt.isBefore(OffsetDateTime.now())) {
             SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Scheduling);
             sce.getElements().add("Cannot schedule in the past");
@@ -270,12 +267,14 @@
                         propagationTask.getPropagationData());
                 taskInfo.setOldConnObjectKey(propagationTask.getOldConnObjectKey());
 
-                TaskExec propExec = taskExecutor.execute(taskInfo, new DefaultPropagationReporter(), executor);
+                TaskExec<PropagationTask> propExec = taskExecutor.execute(
+                        taskInfo, new DefaultPropagationReporter(), executor);
                 result = binder.getExecTO(propExec);
                 break;
 
             case NOTIFICATION:
-                TaskExec notExec = notificationJobDelegate.executeSingle((NotificationTask) task, executor);
+                TaskExec<NotificationTask> notExec = notificationJobDelegate.executeSingle(
+                        (NotificationTask) task, executor);
                 result = binder.getExecTO(notExec);
                 break;
 
@@ -326,7 +325,7 @@
 
     @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_DELETE + "')")
     public <T extends TaskTO> T delete(final TaskType type, final String key) {
-        Task task = taskDAO.find(key);
+        Task<?> task = taskDAO.find(type, key);
         if (task == null) {
             throw new NotFoundException("Task " + key);
         }
@@ -356,12 +355,9 @@
     public Pair<Integer, List<ExecTO>> listExecutions(
             final String key, final int page, final int size, final List<OrderByClause> orderByClauses) {
 
-        Task task = taskDAO.find(key);
-        if (task == null) {
-            throw new NotFoundException("Task " + key);
-        }
+        Task<?> task = taskDAO.find(key).orElseThrow(() -> new NotFoundException("Task " + key));
 
-        Integer count = taskExecDAO.count(key);
+        Integer count = taskExecDAO.count(task);
 
         List<ExecTO> result = taskExecDAO.findAll(task, page, size, orderByClauses).stream().
                 map(taskExec -> binder.getExecTO(taskExec)).collect(Collectors.toList());
@@ -379,10 +375,8 @@
     @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_DELETE + "')")
     @Override
     public ExecTO deleteExecution(final String execKey) {
-        TaskExec taskExec = taskExecDAO.find(execKey);
-        if (taskExec == null) {
-            throw new NotFoundException("Task execution " + execKey);
-        }
+        TaskExec<?> taskExec = taskExecDAO.find(execKey).
+                orElseThrow(() -> new NotFoundException("Task execution " + execKey));
 
         ExecTO taskExecutionToDelete = binder.getExecTO(taskExec);
         taskExecDAO.delete(taskExec);
@@ -398,10 +392,7 @@
             final OffsetDateTime endedBefore,
             final OffsetDateTime endedAfter) {
 
-        Task task = taskDAO.find(key);
-        if (task == null) {
-            throw new NotFoundException("Task " + key);
-        }
+        Task<?> task = taskDAO.find(key).orElseThrow(() -> new NotFoundException("Task " + key));
 
         List<BatchResponseItem> batchResponseItems = new ArrayList<>();
 
@@ -427,7 +418,7 @@
     protected Triple<JobType, String, String> getReference(final JobKey jobKey) {
         String key = JobNamer.getTaskKeyFromJobName(jobKey.getName());
 
-        Task task = taskDAO.find(key);
+        Task<?> task = taskDAO.find(key).orElse(null);
         return task == null || !(task instanceof SchedTask)
                 ? null
                 : Triple.of(JobType.TASK, key, binder.buildRefDesc(task));
@@ -442,10 +433,7 @@
     @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_READ + "')")
     @Override
     public JobTO getJob(final String key) {
-        Task task = taskDAO.find(key);
-        if (task == null) {
-            throw new NotFoundException("Task " + key);
-        }
+        Task<?> task = taskDAO.find(key).orElseThrow(() -> new NotFoundException("Task " + key));
 
         JobTO jobTO = null;
         try {
@@ -466,10 +454,7 @@
     @PreAuthorize("hasRole('" + IdRepoEntitlement.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);
-        }
+        Task<?> task = taskDAO.find(key).orElseThrow(() -> new NotFoundException("Task " + key));
 
         doActionJob(JobNamer.getJobKey(task), action);
     }
@@ -505,8 +490,9 @@
         }
 
         if (key != null) {
+            String taskKey = key;
             try {
-                final Task task = taskDAO.find(key);
+                Task<?> task = taskDAO.find(taskKey).orElseThrow(() -> new NotFoundException("Task " + taskKey));
                 return binder.getTaskTO(task, taskUtilsFactory.getInstance(task), false);
             } catch (Throwable ignore) {
                 LOG.debug("Unresolved reference", ignore);
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/TaskDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/TaskDAO.java
index 5fe5171..6e03859 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/TaskDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/TaskDAO.java
@@ -20,6 +20,7 @@
 
 import java.time.OffsetDateTime;
 import java.util.List;
+import java.util.Optional;
 import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ExecStatus;
@@ -33,13 +34,13 @@
 import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
 import org.apache.syncope.core.persistence.api.entity.task.Task;
 
-public interface TaskDAO extends DAO<Task> {
-
-    Class<? extends Task> getEntityReference(TaskType type);
+public interface TaskDAO extends DAO<Task<?>> {
 
     boolean exists(TaskType type, String key);
 
-    <T extends Task> T find(String key);
+    <T extends Task<T>> T find(TaskType type, String key);
+
+    Optional<Task<?>> find(String key);
 
     List<SchedTask> findByDelegate(Implementation delegate);
 
@@ -49,11 +50,11 @@
 
     List<PushTask> findByPushActions(Implementation pushActions);
 
-    <T extends Task> List<T> findToExec(TaskType type);
+    <T extends Task<T>> List<T> findToExec(TaskType type);
 
-    <T extends Task> List<T> findAll(TaskType type);
+    <T extends Task<T>> List<T> findAll(TaskType type);
 
-    <T extends Task> List<T> findAll(
+    <T extends Task<T>> List<T> findAll(
             TaskType type,
             ExternalResource resource,
             Notification notification,
@@ -70,11 +71,11 @@
             AnyTypeKind anyTypeKind,
             String entityKey);
 
-    <T extends Task> T save(T task);
+    <T extends Task<T>> T save(T task);
 
-    void delete(String key);
+    void delete(TaskType type, String key);
 
-    void delete(Task task);
+    void delete(Task<?> task);
 
     void deleteAll(ExternalResource resource, TaskType type);
 
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 000fdda..55edbc6 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
@@ -20,36 +20,40 @@
 
 import java.time.OffsetDateTime;
 import java.util.List;
+import java.util.Optional;
+import org.apache.syncope.common.lib.types.TaskType;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.entity.task.Task;
 import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 
-public interface TaskExecDAO extends DAO<TaskExec> {
+public interface TaskExecDAO extends DAO<TaskExec<?>> {
 
-    TaskExec find(String key);
+    <T extends Task<T>> TaskExec<T> find(TaskType type, String key);
 
-    List<TaskExec> findRecent(int max);
+    Optional<TaskExec<?>> find(String key);
 
-    <T extends Task> TaskExec findLatestStarted(T task);
+    List<TaskExec<?>> findRecent(int max);
 
-    <T extends Task> TaskExec findLatestEnded(T task);
+    TaskExec<?> findLatestStarted(TaskType type, Task<?> task);
 
-    int count(String taskKey);
+    TaskExec<?> findLatestEnded(TaskType type, Task<?> task);
 
-    <T extends Task> List<TaskExec> findAll(T task, int page, int itemsPerPage, List<OrderByClause> orderByClauses);
+    int count(Task<?> task);
 
-    <T extends Task> List<TaskExec> findAll(
-            T task,
+    List<TaskExec<?>> findAll(Task<?> task, int page, int itemsPerPage, List<OrderByClause> orderByClauses);
+
+    List<TaskExec<?>> findAll(
+            Task<?> task,
             OffsetDateTime startedBefore,
             OffsetDateTime startedAfter,
             OffsetDateTime endedBefore,
             OffsetDateTime endedAfter);
 
-    TaskExec save(TaskExec execution);
+    <T extends Task<T>> TaskExec<T> save(TaskExec<T> execution);
 
-    void saveAndAdd(String taskKey, TaskExec execution);
+    <T extends Task<T>> void saveAndAdd(TaskType type, String taskKey, TaskExec<T> execution);
 
-    void delete(String key);
+    <T extends Task<T>> void delete(TaskType type, String key);
 
-    void delete(TaskExec execution);
+    <T extends Task<T>> void delete(TaskExec<T> execution);
 }
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/EntityFactory.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/EntityFactory.java
index a74c490..ff2fd74 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/EntityFactory.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/EntityFactory.java
@@ -18,15 +18,19 @@
  */
 package org.apache.syncope.core.persistence.api.entity;
 
+import org.apache.syncope.common.lib.types.TaskType;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
+import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 
 public interface EntityFactory {
 
     <E extends Entity> E newEntity(Class<E> reference);
 
+    <E extends TaskExec<?>> E newTaskExec(TaskType taskType);
+
     ConnPoolConf newConnPoolConf();
 
     Class<? extends User> userClass();
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/NotificationTask.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/NotificationTask.java
index d13f748..2b4c948 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/NotificationTask.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/NotificationTask.java
@@ -23,7 +23,7 @@
 import org.apache.syncope.common.lib.types.TraceLevel;
 import org.apache.syncope.core.persistence.api.entity.Notification;
 
-public interface NotificationTask extends Task {
+public interface NotificationTask extends Task<NotificationTask> {
 
     Notification getNotification();
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/PropagationTask.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/PropagationTask.java
index 527f27f..387f425 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/PropagationTask.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/PropagationTask.java
@@ -22,7 +22,7 @@
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.core.persistence.api.entity.ExternalResource;
 
-public interface PropagationTask extends Task {
+public interface PropagationTask extends Task<PropagationTask> {
 
     String getConnObjectKey();
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/ProvisioningTask.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/ProvisioningTask.java
index e7f9fd1..0854ed8 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/ProvisioningTask.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/ProvisioningTask.java
@@ -24,7 +24,7 @@
 import org.apache.syncope.core.persistence.api.entity.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
 
-public interface ProvisioningTask extends SchedTask {
+public interface ProvisioningTask<T extends SchedTask> extends SchedTask {
 
     ExternalResource getResource();
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/PullTask.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/PullTask.java
index 7e932de..970ee95 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/PullTask.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/PullTask.java
@@ -24,7 +24,7 @@
 import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 
-public interface PullTask extends ProvisioningTask {
+public interface PullTask extends ProvisioningTask<PullTask> {
 
     PullMode getPullMode();
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/PushTask.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/PushTask.java
index b5fa504..d831221 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/PushTask.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/PushTask.java
@@ -23,7 +23,7 @@
 import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 
-public interface PushTask extends ProvisioningTask {
+public interface PushTask extends ProvisioningTask<PushTask> {
 
     Realm getSourceRealm();
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/SchedTask.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/SchedTask.java
index 4174b25..dd48029 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/SchedTask.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/SchedTask.java
@@ -21,7 +21,7 @@
 import java.time.OffsetDateTime;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
 
-public interface SchedTask extends Task {
+public interface SchedTask extends Task<SchedTask> {
 
     void setName(String name);
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/Task.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/Task.java
index 708c2c8..1fe42e0 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/Task.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/Task.java
@@ -21,9 +21,9 @@
 import java.util.List;
 import org.apache.syncope.core.persistence.api.entity.Entity;
 
-public interface Task extends Entity {
+public interface Task<T extends Task<T>> extends Entity {
 
-    boolean add(TaskExec exec);
+    boolean add(TaskExec<T> exec);
 
-    List<? extends TaskExec> getExecs();
+    List<? extends TaskExec<T>> getExecs();
 }
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/TaskExec.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/TaskExec.java
index 7cb875c..569aa3a 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/TaskExec.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/TaskExec.java
@@ -20,9 +20,9 @@
 
 import org.apache.syncope.core.persistence.api.entity.Exec;
 
-public interface TaskExec extends Exec {
+public interface TaskExec<T extends Task<T>> extends Exec {
 
-    Task getTask();
+    T getTask();
 
-    void setTask(Task task);
+    void setTask(T task);
 }
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/TaskUtils.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/TaskUtils.java
index 96bde02..e8f5c15 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/TaskUtils.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/TaskUtils.java
@@ -25,12 +25,11 @@
 
     TaskType getType();
 
-    <T extends Task> T newTask();
+    <T extends Task<T>> T newTask();
 
     <T extends TaskTO> T newTaskTO();
 
-    <T extends Task> Class<T> taskClass();
+    <T extends Task<T>> Class<T> taskClass();
 
     <T extends TaskTO> Class<T> taskTOClass();
-
 }
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/TaskUtilsFactory.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/TaskUtilsFactory.java
index 78cbfcb..9e9e88a 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/TaskUtilsFactory.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/TaskUtilsFactory.java
@@ -25,7 +25,7 @@
 
     TaskUtils getInstance(TaskType type);
 
-    TaskUtils getInstance(Task task);
+    TaskUtils getInstance(Task<?> task);
 
     TaskUtils getInstance(Class<? extends TaskTO> taskClass);
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/User.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/User.java
index c7f2214..f704859 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/User.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/User.java
@@ -47,6 +47,10 @@
 
     void setChangePwdDate(OffsetDateTime changePwdDate);
 
+    void addToPasswordHistory(String password);
+
+    void removeOldestEntriesFromPasswordHistory(int n);
+
     List<String> getPasswordHistory();
 
     SecurityQuestion getSecurityQuestion();
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java
index c9a209d..88553bd 100644
--- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java
@@ -360,7 +360,7 @@
         }
 
         query.append("SELECT DISTINCT any_id FROM ").
-                append(svs.dynrolemembership().name).append(" WHERE ").
+                append(SearchSupport.dynrolemembership().name).append(" WHERE ").
                 append("role_id=?").append(setParameter(parameters, cond.getRole())).
                 append(')');
 
@@ -421,7 +421,7 @@
         }
 
         query.append("SELECT DISTINCT any_id FROM ").
-                append(svs.dynrealmmembership().name).append(" WHERE ").
+                append(SearchSupport.dynrealmmembership().name).append(" WHERE ").
                 append("dynRealm_id=?").append(setParameter(parameters, cond.getDynRealm())).
                 append(')');
 
diff --git a/core/persistence-jpa-json/src/main/resources/domains/jpa-json/MasterContent.xml b/core/persistence-jpa-json/src/main/resources/domains/jpa-json/MasterContent.xml
index afd2ce0..bfb31ff 100644
--- a/core/persistence-jpa-json/src/main/resources/domains/jpa-json/MasterContent.xml
+++ b/core/persistence-jpa-json/src/main/resources/domains/jpa-json/MasterContent.xml
@@ -47,11 +47,11 @@
 
   <Implementation id="ExpiredAccessTokenCleanup" type="TASKJOB_DELEGATE" engine="JAVA"
                   body="org.apache.syncope.core.provisioning.java.job.ExpiredAccessTokenCleanup"/>
-  <Task DTYPE="SchedTask" id="89de5014-e3f5-4462-84d8-d97575740baf" name="Access Token Cleanup Task"  active="1"
+  <SchedTask id="89de5014-e3f5-4462-84d8-d97575740baf" name="Access Token Cleanup Task"  active="1"
         jobDelegate_id="ExpiredAccessTokenCleanup" cronExpression="0 0/5 * * * ?"/>
   <Implementation id="ExpiredBatchCleanup" type="TASKJOB_DELEGATE" engine="JAVA"
                   body="org.apache.syncope.core.provisioning.java.job.ExpiredBatchCleanup"/>
-  <Task DTYPE="SchedTask" id="8ea0ea51-ce08-4fe3-a0c8-c281b31b5893" name="Expired Batch Operations Cleanup Task"  active="1"
+  <SchedTask id="8ea0ea51-ce08-4fe3-a0c8-c281b31b5893" name="Expired Batch Operations Cleanup Task"  active="1"
         jobDelegate_id="ExpiredBatchCleanup" cronExpression="0 0/5 * * * ?"/>
 
   <!-- Password reset notifications -->
@@ -95,14 +95,12 @@
 
   <Notification id="e00945b5-1184-4d43-8e45-4318a8dcdfd4" active="1" recipientAttrName="email" selfAsRecipient="1" 
                 sender="admin@syncope.apache.org" subject="Password Reset request" template_id="requestPasswordReset" 
-                traceLevel="FAILURES"/> 
+                traceLevel="FAILURES" events='["[CUSTOM]:[]:[]:[requestPasswordReset]:[SUCCESS]"]'/> 
   <AnyAbout id="a328f2e6-25e9-4cc1-badf-7425d7be4b39" anyType_id="USER" notification_id="e00945b5-1184-4d43-8e45-4318a8dcdfd4" anyType_filter="token!=$null"/>
-  <Notification_events notification_id="e00945b5-1184-4d43-8e45-4318a8dcdfd4" event="[CUSTOM]:[]:[]:[requestPasswordReset]:[SUCCESS]"/>
   
   <Notification id="bef0c250-e8a7-4848-bb63-2564fc409ce2" active="1" recipientAttrName="email" selfAsRecipient="1" 
                 sender="admin@syncope.apache.org" subject="Password Reset successful" template_id="confirmPasswordReset" 
-                traceLevel="FAILURES"/> 
-  <Notification_events notification_id="bef0c250-e8a7-4848-bb63-2564fc409ce2" event="[CUSTOM]:[]:[]:[confirmPasswordReset]:[SUCCESS]"/>
+                traceLevel="FAILURES" events='["[CUSTOM]:[]:[]:[confirmPasswordReset]:[SUCCESS]"]'/> 
 
   <ReportTemplate id="empty"/>  
 
@@ -111,20 +109,5 @@
                   body='{"_class":"org.apache.syncope.common.lib.report.ReconciliationReportletConf","name":"dashboardReconciliationReportlet","userMatchingCond":null,"groupMatchingCond":null,"anyObjectMatchingCond":null,"features":["key","username","groupName"]}'/>
   <ReportReportlet report_id="c3520ad9-179f-49e7-b315-d684d216dd97" implementation_id="ReconciliationReportletConf"/>
 
-  <SyncopeRole id="GROUP_OWNER"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_SEARCH"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_CREATE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_UPDATE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_DELETE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPECLASS_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPE_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPECLASS_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="RELATIONSHIPTYPE_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPE_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="REALM_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_SEARCH"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_UPDATE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_DELETE"/>
+  <SyncopeRole id="GROUP_OWNER" entitlements='["USER_SEARCH","USER_READ","USER_CREATE","USER_UPDATE","USER_DELETE","ANYTYPECLASS_READ","ANYTYPE_LIST","ANYTYPECLASS_LIST","RELATIONSHIPTYPE_LIST","ANYTYPE_READ","REALM_LIST","GROUP_SEARCH","GROUP_READ","GROUP_UPDATE","GROUP_DELETE"]'/>
 </dataset>
diff --git a/core/persistence-jpa-json/src/main/resources/myjson/indexes.xml b/core/persistence-jpa-json/src/main/resources/myjson/indexes.xml
index eae2a73..066bad3 100644
--- a/core/persistence-jpa-json/src/main/resources/myjson/indexes.xml
+++ b/core/persistence-jpa-json/src/main/resources/myjson/indexes.xml
@@ -42,8 +42,11 @@
   <entry key="ARelationship_RightIndex">CREATE INDEX ARelationship_RightIndex ON ARelationship(right_anyObject_id)</entry>
   <entry key="ARelationship_AnyObjectIndex">CREATE INDEX ARelationship_AnyObjectIndex ON ARelationship(left_anyObject_id)</entry>
 
-  <entry key="Task_executedIndex">CREATE INDEX Task_executedIndex ON Task(executed)</entry>
-  <entry key="TaskExec_TaskIdIndex">CREATE INDEX TaskExec_TaskIdIndex ON TaskExec(task_id)</entry>
+  <entry key="Task_executedIndex">CREATE INDEX Task_executedIndex ON NotificationTask(executed)</entry>
+  <entry key="TaskExec1_TaskIdIndex">CREATE INDEX TaskExec1_TaskIdIndex ON PropagationTaskExec(task_id)</entry>
+  <entry key="TaskExec2_TaskIdIndex">CREATE INDEX TaskExec2_TaskIdIndex ON PullTaskExec(task_id)</entry>
+  <entry key="TaskExec3_TaskIdIndex">CREATE INDEX TaskExec3_TaskIdIndex ON PushTaskExec(task_id)</entry>
+  <entry key="TaskExec4_TaskIdIndex">CREATE INDEX TaskExec4_TaskIdIndex ON NotificationTaskExec(task_id)</entry>
+  <entry key="TaskExec5_TaskIdIndex">CREATE INDEX TaskExec5_TaskIdIndex ON SchedTaskExec(task_id)</entry>
   <entry key="ATPullTask_PullTaskIndex">CREATE INDEX ATPullTask_PullTaskIndex ON AnyTemplatePullTask(pullTask_id)</entry>
-  <entry key="NT_recipientsIndex">CREATE INDEX NT_recipientsIndex ON NotificationTask_recipients(notificationTask_id)</entry>
 </properties>
diff --git a/core/persistence-jpa-json/src/main/resources/pgjsonb/indexes.xml b/core/persistence-jpa-json/src/main/resources/pgjsonb/indexes.xml
index de3758a..8fc0de0 100644
--- a/core/persistence-jpa-json/src/main/resources/pgjsonb/indexes.xml
+++ b/core/persistence-jpa-json/src/main/resources/pgjsonb/indexes.xml
@@ -46,8 +46,11 @@
   <entry key="ARelationship_RightIndex">CREATE INDEX ARelationship_RightIndex ON ARelationship(right_anyObject_id)</entry>
   <entry key="ARelationship_AnyObjectIndex">CREATE INDEX ARelationship_AnyObjectIndex ON ARelationship(left_anyObject_id)</entry>
 
-  <entry key="Task_executedIndex">CREATE INDEX Task_executedIndex ON Task(executed)</entry>
-  <entry key="TaskExec_TaskIdIndex">CREATE INDEX TaskExec_TaskIdIndex ON TaskExec(task_id)</entry>
+  <entry key="Task_executedIndex">CREATE INDEX Task_executedIndex ON NotificationTask(executed)</entry>
+  <entry key="TaskExec1_TaskIdIndex">CREATE INDEX TaskExec1_TaskIdIndex ON PropagationTaskExec(task_id)</entry>
+  <entry key="TaskExec2_TaskIdIndex">CREATE INDEX TaskExec2_TaskIdIndex ON PullTaskExec(task_id)</entry>
+  <entry key="TaskExec3_TaskIdIndex">CREATE INDEX TaskExec3_TaskIdIndex ON PushTaskExec(task_id)</entry>
+  <entry key="TaskExec4_TaskIdIndex">CREATE INDEX TaskExec4_TaskIdIndex ON NotificationTaskExec(task_id)</entry>
+  <entry key="TaskExec5_TaskIdIndex">CREATE INDEX TaskExec5_TaskIdIndex ON SchedTaskExec(task_id)</entry>
   <entry key="ATPullTask_PullTaskIndex">CREATE INDEX ATPullTask_PullTaskIndex ON AnyTemplatePullTask(pullTask_id)</entry>
-  <entry key="NT_recipientsIndex">CREATE INDEX NT_recipientsIndex ON NotificationTask_recipients(notificationTask_id)</entry>
 </properties>
diff --git a/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
index b959f52..18e866d 100644
--- a/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
@@ -287,51 +287,21 @@
              spec="{ &quot;method&quot;: &quot;GET&quot;, &quot;url&quot;: &quot;/a/b/c&quot; }"
              application_id="mightyApp"/>
 
-  <SyncopeRole id="User reviewer"/>
-  <SyncopeRole_entitlements entitlement="USER_READ" role_id="User reviewer"/>
-  <SyncopeRole_entitlements entitlement="USER_LIST" role_id="User reviewer"/>
-  <SyncopeRole_entitlements entitlement="USER_SEARCH" role_id="User reviewer"/>
-  <SyncopeRole_entitlements entitlement="ANYTYPE_LIST" role_id="User reviewer"/>
-  <SyncopeRole_entitlements entitlement="ANYTYPE_READ" role_id="User reviewer"/>
-  <SyncopeRole_entitlements entitlement="ANYTYPECLASS_LIST" role_id="User reviewer"/>
-  <SyncopeRole_entitlements entitlement="ANYTYPECLASS_READ" role_id="User reviewer"/>
+  <SyncopeRole id="User reviewer" entitlements='["USER_READ","USER_LIST","USER_SEARCH","ANYTYPE_LIST","ANYTYPE_READ","ANYTYPECLASS_LIST","ANYTYPECLASS_READ"]'/>
   <SyncopeRole_Realm role_id="User reviewer" realm_id="722f3d84-9c2b-4525-8f6e-e4b82c55a36c"/>
   <SyncopeRole_Realm role_id="User reviewer" realm_id="c5b75db1-fce7-470f-b780-3b9934d82a9d"/>
   
-  <SyncopeRole id="User manager"/>
-  <SyncopeRole_entitlements entitlement="USER_READ" role_id="User manager"/>
-  <SyncopeRole_entitlements entitlement="USER_LIST" role_id="User manager"/>
-  <SyncopeRole_entitlements entitlement="USER_SEARCH" role_id="User manager"/>
-  <SyncopeRole_entitlements entitlement="ANYTYPE_LIST" role_id="User manager"/>
-  <SyncopeRole_entitlements entitlement="ANYTYPE_READ" role_id="User manager"/>
-  <SyncopeRole_entitlements entitlement="ANYTYPECLASS_LIST" role_id="User manager"/>
-  <SyncopeRole_entitlements entitlement="ANYTYPECLASS_READ" role_id="User manager"/>
-  <SyncopeRole_entitlements entitlement="USER_REQUEST_FORM_LIST" role_id="User manager"/>
-  <SyncopeRole_entitlements entitlement="USER_REQUEST_FORM_CLAIM" role_id="User manager"/>
-  <SyncopeRole_entitlements entitlement="USER_REQUEST_FORM_SUBMIT" role_id="User manager"/>
+  <SyncopeRole id="User manager" entitlements='["USER_READ","USER_LIST","USER_SEARCH","ANYTYPE_LIST","ANYTYPE_READ","ANYTYPECLASS_LIST","ANYTYPECLASS_READ","USER_REQUEST_FORM_LIST","USER_REQUEST_FORM_CLAIM","USER_REQUEST_FORM_SUBMIT"]'/>
   <SyncopeRole_Realm role_id="User manager" realm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"/>
 
-  <SyncopeRole id="Other"/>
-  <SyncopeRole_entitlements entitlement="SCHEMA_READ" role_id="Other"/>
-  <SyncopeRole_entitlements entitlement="GROUP_READ" role_id="Other"/>
-  <SyncopeRole_entitlements entitlement="USER_REQUEST_FORM_CLAIM" role_id="Other"/>
+  <SyncopeRole id="Other" entitlements='["SCHEMA_READ","GROUP_READ","USER_REQUEST_FORM_CLAIM"]'/>
   <SyncopeRole_Realm role_id="Other" realm_id="722f3d84-9c2b-4525-8f6e-e4b82c55a36c"/>
   <SyncopeRole_Privilege role_id="Other" privilege_id="postMighty"/>
   
-  <SyncopeRole id="Search for realm evenTwo"/>
-  <SyncopeRole_entitlements entitlement="USER_READ" role_id="Search for realm evenTwo"/>
-  <SyncopeRole_entitlements entitlement="USER_SEARCH" role_id="Search for realm evenTwo"/>
+  <SyncopeRole id="Search for realm evenTwo" entitlements='["USER_READ","USER_SEARCH"]'/>
   <SyncopeRole_Realm role_id="Search for realm evenTwo" realm_id="0679e069-7355-4b20-bd11-a5a0a5453c7c"/>
 
-  <SyncopeRole id="Connector and Resource for realm evenTwo"/>
-  <SyncopeRole_entitlements entitlement="CONNECTOR_READ" role_id="Connector and Resource for realm evenTwo"/>
-  <SyncopeRole_entitlements entitlement="CONNECTOR_UPDATE" role_id="Connector and Resource for realm evenTwo"/>
-  <SyncopeRole_entitlements entitlement="CONNECTOR_DELETE" role_id="Connector and Resource for realm evenTwo"/>
-  <SyncopeRole_entitlements entitlement="CONNECTOR_LIST" role_id="Connector and Resource for realm evenTwo"/>
-  <SyncopeRole_entitlements entitlement="RESOURCE_READ" role_id="Connector and Resource for realm evenTwo"/>
-  <SyncopeRole_entitlements entitlement="RESOURCE_UPDATE" role_id="Connector and Resource for realm evenTwo"/>
-  <SyncopeRole_entitlements entitlement="RESOURCE_DELETE" role_id="Connector and Resource for realm evenTwo"/>
-  <SyncopeRole_entitlements entitlement="RESOURCE_LIST" role_id="Connector and Resource for realm evenTwo"/>
+  <SyncopeRole id="Connector and Resource for realm evenTwo" entitlements='["CONNECTOR_READ","CONNECTOR_UPDATE","CONNECTOR_DELETE","CONNECTOR_LIST","RESOURCE_READ","RESOURCE_UPDATE","RESOURCE_DELETE","RESOURCE_LIST"]'/>
   <SyncopeRole_Realm role_id="Connector and Resource for realm evenTwo"
                      realm_id="0679e069-7355-4b20-bd11-a5a0a5453c7c"/>
 
@@ -714,25 +684,25 @@
                   body="org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate"/>
   <Implementation id="PushJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
                   body="org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate"/>
-  <Task DTYPE="PropagationTask" id="1e697572-b896-484c-ae7f-0c8f63fcbc6c" operation="UPDATE"
-        objectClassName="__ACCOUNT__" resource_id="ws-target-resource-2" anyTypeKind="USER" entityKey="1417acbe-cbf6-4277-9372-e75e04f97000"
-        propagationData='{"attributes":[{"name":"__PASSWORD__","value":[{"readOnly":false,"disposed":false,"encryptedBytes":"m9nh2US0Sa6m+cXccCq0Xw==","base64SHA1Hash":"GFJ69qfjxEOdrmt+9q+0Cw2uz60="}]},{"name":"__NAME__","value":["userId"],"nameValue":"userId"},{"name":"fullname","value":["fullname"]},{"name":"type","value":["type"]}]}'/>
-  <TaskExec id="e58ca1c7-178a-4012-8a71-8aa14eaf0655" task_id="1e697572-b896-484c-ae7f-0c8f63fcbc6c" startDate="2015-12-17 09:40:00" endDate="2015-12-17 09:42:00" status="SUCCESS"/>
-  <Task DTYPE="PropagationTask" id="b8870cfb-3c1e-4fc4-abcb-2559826232e6" operation="CREATE"
-        objectClassName="__ACCOUNT__" resource_id="ws-target-resource-2" anyTypeKind="USER" entityKey="1417acbe-cbf6-4277-9372-e75e04f97000"
-        propagationData='{"attributes":[{"name":"__PASSWORD__","value":[{"readOnly":false,"disposed":false,"encryptedBytes":"m9nh2US0Sa6m+cXccCq0Xw==","base64SHA1Hash":"GFJ69qfjxEOdrmt+9q+0Cw2uz60="}]},{"name":"__NAME__","value":["userId"],"nameValue":"userId"},{"name":"fullname","value":["fullname"]},{"name":"type","value":["type"]}]}'/>
-  <Task DTYPE="PropagationTask" id="316285cc-ae52-4ea2-a33b-7355e189ac3f" operation="DELETE"
-        objectClassName="__ACCOUNT__" resource_id="ws-target-resource-2" anyTypeKind="USER" entityKey="1417acbe-cbf6-4277-9372-e75e04f97000"
-        propagationData='{"attributes":[{"name":"__PASSWORD__","value":[{"readOnly":false,"disposed":false,"encryptedBytes":"m9nh2US0Sa6m+cXccCq0Xw==","base64SHA1Hash":"GFJ69qfjxEOdrmt+9q+0Cw2uz60="}]},{"name":"__NAME__","value":["userId"],"nameValue":"userId"},{"name":"type","value":["type"]}]}'/>
+  <PropagationTask id="1e697572-b896-484c-ae7f-0c8f63fcbc6c" operation="UPDATE"
+                   objectClassName="__ACCOUNT__" resource_id="ws-target-resource-2" anyTypeKind="USER" entityKey="1417acbe-cbf6-4277-9372-e75e04f97000"
+                   propagationData='{"attributes":[{"name":"__PASSWORD__","value":[{"readOnly":false,"disposed":false,"encryptedBytes":"m9nh2US0Sa6m+cXccCq0Xw==","base64SHA1Hash":"GFJ69qfjxEOdrmt+9q+0Cw2uz60="}]},{"name":"__NAME__","value":["userId"],"nameValue":"userId"},{"name":"fullname","value":["fullname"]},{"name":"type","value":["type"]}]}'/>
+  <PropagationTaskExec id="e58ca1c7-178a-4012-8a71-8aa14eaf0655" task_id="1e697572-b896-484c-ae7f-0c8f63fcbc6c" startDate="2015-12-17 09:40:00" endDate="2015-12-17 09:42:00" status="SUCCESS"/>
+  <PropagationTask id="b8870cfb-3c1e-4fc4-abcb-2559826232e6" operation="CREATE"
+                   objectClassName="__ACCOUNT__" resource_id="ws-target-resource-2" anyTypeKind="USER" entityKey="1417acbe-cbf6-4277-9372-e75e04f97000"
+                   propagationData='{"attributes":[{"name":"__PASSWORD__","value":[{"readOnly":false,"disposed":false,"encryptedBytes":"m9nh2US0Sa6m+cXccCq0Xw==","base64SHA1Hash":"GFJ69qfjxEOdrmt+9q+0Cw2uz60="}]},{"name":"__NAME__","value":["userId"],"nameValue":"userId"},{"name":"fullname","value":["fullname"]},{"name":"type","value":["type"]}]}'/>
+  <PropagationTask id="316285cc-ae52-4ea2-a33b-7355e189ac3f" operation="DELETE"
+                   objectClassName="__ACCOUNT__" resource_id="ws-target-resource-2" anyTypeKind="USER" entityKey="1417acbe-cbf6-4277-9372-e75e04f97000"
+                   propagationData='{"attributes":[{"name":"__PASSWORD__","value":[{"readOnly":false,"disposed":false,"encryptedBytes":"m9nh2US0Sa6m+cXccCq0Xw==","base64SHA1Hash":"GFJ69qfjxEOdrmt+9q+0Cw2uz60="}]},{"name":"__NAME__","value":["userId"],"nameValue":"userId"},{"name":"type","value":["type"]}]}'/>
   <!--SYNCOPE-1641 to be purged-->
-  <Task DTYPE="PropagationTask" id="025c956d-ea88-4bd7-9e44-2f35e0aa7055" operation="UPDATE"
-        objectClassName="__ACCOUNT__" resource_id="ws-target-resource-1" anyTypeKind="USER" entityKey="1417acbe-cbf6-4277-9372-e75e04f97000"
-        propagationData='{"attributes":[{"name":"__PASSWORD__","value":[{"readOnly":false,"disposed":false,"encryptedBytes":"m9nh2US0Sa6m+cXccCq0Xw==","base64SHA1Hash":"GFJ69qfjxEOdrmt+9q+0Cw2uz60="}]},{"name":"__NAME__","value":["userId"],"nameValue":"userId"},{"name":"fullname","value":["fullname"]},{"name":"type","value":["type"]}]}'/>
-  <TaskExec id="c3290f8b-caf9-4a85-84fb-fb619b65cd49" task_id="025c956d-ea88-4bd7-9e44-2f35e0aa7055" startDate="2015-12-17 09:40:00" endDate="2015-12-17 09:42:00" status="SUCCESS"/>
-  <Task DTYPE="PullTask" remediation="0" id="c41b9b71-9bfa-4f90-89f2-84787def4c5c" name="CSV (update matching; assign unmatching)" resource_id="resource-csv"
-        destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"
-        pullMode="INCREMENTAL" unmatchingRule="ASSIGN" matchingRule="UPDATE" active="1"
-        jobDelegate_id="PullJobDelegate"/>
+  <PropagationTask id="025c956d-ea88-4bd7-9e44-2f35e0aa7055" operation="UPDATE"
+                   objectClassName="__ACCOUNT__" resource_id="ws-target-resource-1" anyTypeKind="USER" entityKey="1417acbe-cbf6-4277-9372-e75e04f97000"
+                   propagationData='{"attributes":[{"name":"__PASSWORD__","value":[{"readOnly":false,"disposed":false,"encryptedBytes":"m9nh2US0Sa6m+cXccCq0Xw==","base64SHA1Hash":"GFJ69qfjxEOdrmt+9q+0Cw2uz60="}]},{"name":"__NAME__","value":["userId"],"nameValue":"userId"},{"name":"fullname","value":["fullname"]},{"name":"type","value":["type"]}]}'/>
+  <PropagationTaskExec id="c3290f8b-caf9-4a85-84fb-fb619b65cd49" task_id="025c956d-ea88-4bd7-9e44-2f35e0aa7055" startDate="2015-12-17 09:40:00" endDate="2015-12-17 09:42:00" status="SUCCESS"/>
+  <PullTask remediation="0" id="c41b9b71-9bfa-4f90-89f2-84787def4c5c" name="CSV (update matching; assign unmatching)" resource_id="resource-csv"
+            destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"
+            pullMode="INCREMENTAL" unmatchingRule="ASSIGN" matchingRule="UPDATE" active="1"
+            jobDelegate_id="PullJobDelegate"/>
   <AnyTemplatePullTask id="3a6173a9-8c34-4e37-b3b1-0c2ea385fac0"
                        pullTask_id="c41b9b71-9bfa-4f90-89f2-84787def4c5c" anyType_id="USER"
                        template='{"_class":"org.apache.syncope.common.lib.to.UserTO","creator":null,"creationDate":null,"lastModifier":null,"lastChangeDate":null,"key":null,"type":"USER","realm":null,"status":null,"password":null,"token":null,"tokenExpireTime":null,"username":null,"lastLoginDate":null,"changePwdDate":null,"failedLogins":null,"securityQuestion":null,"securityAnswer":null,"auxClasses":["csv"],"derAttrs":[{"schema":"cn","values":[""]}],"virAttrs":[],"resources":["resource-testdb"],"relationships":[],"memberships":[{"groupKey":"f779c0d4-633b-4be5-8f57-32eb478a3ca5","groupName":null}],"dynMemberships":[],"roles":[],"dynRoles":[],"plainAttrs":[{"schema":"ctype","values":["email == &apos;test8@syncope.apache.org&apos;? &apos;TYPE_8&apos;: &apos;TYPE_OTHER&apos;"]}]}'/>
@@ -741,32 +711,32 @@
                        template='{"_class":"org.apache.syncope.common.lib.to.GroupTO","creator":null,"creationDate":null,"lastModifier":null,"lastChangeDate":null,"key":null,"type":"GROUP","realm":null,"status":null,"name":null,"userOwner":null,"groupOwner":null,"udynMembershipCond":null,"auxClasses":[],"derAttrs":[],"virAttrs":[],"resources":[],"plainAttrs":[]}'/>
   <Implementation id="TestSampleJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
                   body="org.apache.syncope.fit.core.reference.TestSampleJobDelegate"/>
-  <Task DTYPE="SchedTask" id="e95555d2-1b09-42c8-b25b-f4c4ec597979" name="SampleJob Task" active="1"
-        jobDelegate_id="TestSampleJobDelegate" cronExpression="0 0 0 1 * ?"/>
+  <SchedTask id="e95555d2-1b09-42c8-b25b-f4c4ec597979" name="SampleJob Task" active="1"
+             jobDelegate_id="TestSampleJobDelegate" cronExpression="0 0 0 1 * ?"/>
   <Implementation id="ExpiredAccessTokenCleanup" type="TASKJOB_DELEGATE" engine="JAVA"
                   body="org.apache.syncope.core.provisioning.java.job.ExpiredAccessTokenCleanup"/>
-  <Task DTYPE="SchedTask" id="89de5014-e3f5-4462-84d8-d97575740baf" name="Access Token Cleanup Task"  active="1"
-        jobDelegate_id="ExpiredAccessTokenCleanup" cronExpression="0 0/5 * * * ?"/>
-  <Task DTYPE="PropagationTask" id="d6c2d6d3-6329-44c1-9187-f1469ead1cfa" operation="UPDATE"
-        objectClassName="__ACCOUNT__" resource_id="ws-target-resource-nopropagation" anyTypeKind="USER" entityKey="1417acbe-cbf6-4277-9372-e75e04f97000"
-        propagationData='{"attributes":[{"name":"__PASSWORD__","value":[{"readOnly":false,"disposed":false,"encryptedBytes":"m9nh2US0Sa6m+cXccCq0Xw==","base64SHA1Hash":"GFJ69qfjxEOdrmt+9q+0Cw2uz60="}]},{"name":"__NAME__","value":["userId"],"nameValue":"userId"},{"name":"fullname","value":["fullname"]},{"name":"type","value":["type"]}]}'/>
-  <TaskExec id="d789462f-e395-424f-bd8e-0db44a93222f" task_id="d6c2d6d3-6329-44c1-9187-f1469ead1cfa" startDate="2015-12-17 09:40:00" endDate="2015-12-17 09:42:00" status="SUCCESS"/>
-  <Task DTYPE="PullTask" remediation="0" id="83f7e85d-9774-43fe-adba-ccd856312994" name="TestDB Task" resource_id="resource-testdb"
-        destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="0" syncStatus="1" pullMode="FULL_RECONCILIATION"
-        unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
+  <SchedTask id="89de5014-e3f5-4462-84d8-d97575740baf" name="Access Token Cleanup Task" active="1"
+             jobDelegate_id="ExpiredAccessTokenCleanup" cronExpression="0 0/5 * * * ?"/>
+  <PropagationTask id="d6c2d6d3-6329-44c1-9187-f1469ead1cfa" operation="UPDATE"
+                   objectClassName="__ACCOUNT__" resource_id="ws-target-resource-nopropagation" anyTypeKind="USER" entityKey="1417acbe-cbf6-4277-9372-e75e04f97000"
+                   propagationData='{"attributes":[{"name":"__PASSWORD__","value":[{"readOnly":false,"disposed":false,"encryptedBytes":"m9nh2US0Sa6m+cXccCq0Xw==","base64SHA1Hash":"GFJ69qfjxEOdrmt+9q+0Cw2uz60="}]},{"name":"__NAME__","value":["userId"],"nameValue":"userId"},{"name":"fullname","value":["fullname"]},{"name":"type","value":["type"]}]}'/>
+  <PropagationTaskExec id="d789462f-e395-424f-bd8e-0db44a93222f" task_id="d6c2d6d3-6329-44c1-9187-f1469ead1cfa" startDate="2015-12-17 09:40:00" endDate="2015-12-17 09:42:00" status="SUCCESS"/>
+  <PullTask remediation="0" id="83f7e85d-9774-43fe-adba-ccd856312994" name="TestDB Task" resource_id="resource-testdb"
+            destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="0" syncStatus="1" pullMode="FULL_RECONCILIATION"
+            unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
   <AnyTemplatePullTask id="6c3f578d-327b-4a7c-8037-6f5ba24eb770" pullTask_id="83f7e85d-9774-43fe-adba-ccd856312994" anyType_id="USER"
                        template='{"_class":"org.apache.syncope.common.lib.to.UserTO","creator":null,"creationDate":null,"lastModifier":null,"lastChangeDate":null,"key":null,"type":"USER","realm":null,"status":null,"password":null,"token":null,"tokenExpireTime":null,"username":null,"lastLoginDate":null,"changePwdDate":null,"failedLogins":null,"securityQuestion":null,"securityAnswer":null,"auxClasses":[],"derAttrs":[],"virAttrs":[],"resources":[],"relationships":[],"memberships":[],"dynMemberships":[],"roles":[],"dynRoles":[],"plainAttrs":[{"schema":"ctype","values":["&apos;type a&apos;"]},{"schema":"userId","values":["&apos;reconciled@syncope.apache.org&apos;"]},{"schema":"fullname","values":["&apos;reconciled fullname&apos;"]},{"schema":"surname","values":["&apos;surname&apos;"]}]}'/>
   <AnyTemplatePullTask id="45b61137-c7c3-49ee-86e0-9efffa75ae68" pullTask_id="83f7e85d-9774-43fe-adba-ccd856312994" anyType_id="GROUP"
                        template='{"_class":"org.apache.syncope.common.lib.to.GroupTO","creator":null,"creationDate":null,"lastModifier":null,"lastChangeDate":null,"key":null,"type":"GROUP","realm":null,"status":null,"name":null,"userOwner":null,"groupOwner":null,"udynMembershipCond":null,"auxClasses":[],"derAttrs":[],"virAttrs":[],"resources":[],"plainAttrs":[]}'/>
-  <Task DTYPE="PullTask" remediation="0" id="81d88f73-d474-4450-9031-605daa4e313f" name="TestDB2 Task" resource_id="resource-testdb2"
-        destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="0" syncStatus="1" pullMode="FULL_RECONCILIATION"
-        unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
-  <Task DTYPE="PullTask" remediation="0" id="7c2242f4-14af-4ab5-af31-cdae23783655" name="TestDB Pull Task" resource_id="resource-db-pull"
-        destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" pullMode="FULL_RECONCILIATION" performCreate="1" performDelete="1" performUpdate="1" syncStatus="0"
-        unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
-  <Task DTYPE="PullTask" remediation="0" id="1e419ca4-ea81-4493-a14f-28b90113686d" name="LDAP Pull Task" resource_id="resource-ldap"
-        destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" pullMode="FULL_RECONCILIATION" performCreate="1" performDelete="1" performUpdate="1" syncStatus="0"
-        unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
+  <PullTask remediation="0" id="81d88f73-d474-4450-9031-605daa4e313f" name="TestDB2 Task" resource_id="resource-testdb2"
+            destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="0" syncStatus="1" pullMode="FULL_RECONCILIATION"
+            unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
+  <PullTask remediation="0" id="7c2242f4-14af-4ab5-af31-cdae23783655" name="TestDB Pull Task" resource_id="resource-db-pull"
+            destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" pullMode="FULL_RECONCILIATION" performCreate="1" performDelete="1" performUpdate="1" syncStatus="0"
+            unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
+  <PullTask remediation="0" id="1e419ca4-ea81-4493-a14f-28b90113686d" name="LDAP Pull Task" resource_id="resource-ldap"
+            destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" pullMode="FULL_RECONCILIATION" performCreate="1" performDelete="1" performUpdate="1" syncStatus="0"
+            unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
   <AnyTemplatePullTask id="df655a2a-40c0-43b1-a157-3f4988802f58" pullTask_id="1e419ca4-ea81-4493-a14f-28b90113686d" anyType_id="USER"
                        template='{"_class":"org.apache.syncope.common.lib.to.UserTO","creator":null,"creationDate":null,"lastModifier":null,"lastChangeDate":null,"key":null,"type":"USER","realm":"&apos;/&apos; + title","status":null,"password":null,"token":null,"tokenExpireTime":null,"username":null,"lastLoginDate":null,"changePwdDate":null,"failedLogins":null,"securityQuestion":null,"securityAnswer":null,"auxClasses":["minimal group"],"derAttrs":[],"virAttrs":[{"schema":"virtualReadOnly","values":[""]}],"resources":["resource-ldap"],"roles":[],"dynRoles":[],"relationships":[],"memberships":[],"dynMemberships":[],"plainAttrs":[]}'/>
   <AnyTemplatePullTask id="fda22ff3-98f3-42e4-a2ae-cd9a28282d57" pullTask_id="1e419ca4-ea81-4493-a14f-28b90113686d" anyType_id="GROUP"
@@ -774,98 +744,98 @@
   <Implementation id="LDAPMembershipPullActions" type="PULL_ACTIONS"  engine="JAVA"
                   body="org.apache.syncope.core.provisioning.java.pushpull.LDAPMembershipPullActions"/>
   <PullTaskAction task_id="1e419ca4-ea81-4493-a14f-28b90113686d" implementation_id="LDAPMembershipPullActions"/>
-  <Task DTYPE="PullTask" remediation="0" id="38abbf9e-a1a3-40a1-a15f-7d0ac02f47f1" name="VirAttrCache test" resource_id="resource-csv"
-        destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="0" performUpdate="1" performDelete="0" syncStatus="0" pullMode="FULL_RECONCILIATION"
-        unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
-  <Task DTYPE="PushTask" id="af558be4-9d2f-4359-bf85-a554e6e90be1" name="Export on resource-testdb2" resource_id="resource-testdb2"
-        sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
-        performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"         
-        unmatchingRule="ASSIGN" matchingRule="IGNORE" active="1" jobDelegate_id="PushJobDelegate"/>  
+  <PullTask remediation="0" id="38abbf9e-a1a3-40a1-a15f-7d0ac02f47f1" name="VirAttrCache test" resource_id="resource-csv"
+            destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="0" performUpdate="1" performDelete="0" syncStatus="0" pullMode="FULL_RECONCILIATION"
+            unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
+  <PushTask id="af558be4-9d2f-4359-bf85-a554e6e90be1" name="Export on resource-testdb2" resource_id="resource-testdb2"
+            sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
+            performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"         
+            unmatchingRule="ASSIGN" matchingRule="IGNORE" active="1" jobDelegate_id="PushJobDelegate"/>  
   <PushTaskAnyFilter id="1fdcff65-765f-4a6e-98a7-13ef7cca47e2" anyType_id="USER" pushTask_id="af558be4-9d2f-4359-bf85-a554e6e90be1" fiql="surname==Vivaldi"/>
   <PushTaskAnyFilter id="3b564c51-5d64-48b3-8da5-fd4ebc10e0a8" anyType_id="GROUP" pushTask_id="af558be4-9d2f-4359-bf85-a554e6e90be1" fiql="name==_NO_ONE_"/>
-  <Task DTYPE="PushTask" id="97f327b6-2eff-4d35-85e8-d581baaab855" name="Export on resource-testdb2" resource_id="resource-testdb2"
-        sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
-        performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"         
-        unmatchingRule="PROVISION" matchingRule="IGNORE" active="1" jobDelegate_id="PushJobDelegate"/>
+  <PushTask id="97f327b6-2eff-4d35-85e8-d581baaab855" name="Export on resource-testdb2" resource_id="resource-testdb2"
+            sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
+            performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"         
+            unmatchingRule="PROVISION" matchingRule="IGNORE" active="1" jobDelegate_id="PushJobDelegate"/>
   <PushTaskAnyFilter id="199efd21-5e89-46ac-95de-f47e9d0569fc" anyType_id="USER" pushTask_id="97f327b6-2eff-4d35-85e8-d581baaab855" fiql="surname==Bellini"/>
   <PushTaskAnyFilter id="7672a167-77d6-4639-8b1d-0af561293c7d" anyType_id="GROUP" pushTask_id="97f327b6-2eff-4d35-85e8-d581baaab855" fiql="name==_NO_ONE_"/>
-  <Task DTYPE="PushTask" id="03aa2a04-4881-4573-9117-753f81b04865" name="Export on resource-testdb2" resource_id="resource-testdb2"
-        sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
-        performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
-        unmatchingRule="UNLINK" matchingRule="IGNORE" active="1" jobDelegate_id="PushJobDelegate"/>
+  <PushTask id="03aa2a04-4881-4573-9117-753f81b04865" name="Export on resource-testdb2" resource_id="resource-testdb2"
+            sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
+            performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
+            unmatchingRule="UNLINK" matchingRule="IGNORE" active="1" jobDelegate_id="PushJobDelegate"/>
   <PushTaskAnyFilter id="39a11ba6-397a-4c94-8bfe-1f4f757d6501" anyType_id="USER" pushTask_id="03aa2a04-4881-4573-9117-753f81b04865" fiql="surname==Puccini"/>
   <PushTaskAnyFilter id="5bd7501e-8a18-4fbd-a3fe-a1e731ba95db" anyType_id="GROUP" pushTask_id="03aa2a04-4881-4573-9117-753f81b04865" fiql="name==_NO_ONE_"/>
-  <Task DTYPE="PushTask" id="5e5f7c7e-9de7-4c6a-99f1-4df1af959807" name="Export on resource-testdb2" resource_id="resource-testdb2"
-        sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
-        performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"         
-        unmatchingRule="IGNORE" matchingRule="IGNORE" active="1" jobDelegate_id="PushJobDelegate"/>
+  <PushTask id="5e5f7c7e-9de7-4c6a-99f1-4df1af959807" name="Export on resource-testdb2" resource_id="resource-testdb2"
+            sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
+            performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"         
+            unmatchingRule="IGNORE" matchingRule="IGNORE" active="1" jobDelegate_id="PushJobDelegate"/>
   <PushTaskAnyFilter id="0d0371a3-5772-4b4c-ad14-139adf1d346a" anyType_id="USER" pushTask_id="5e5f7c7e-9de7-4c6a-99f1-4df1af959807" fiql="surname==Verdi"/>
   <PushTaskAnyFilter id="2e7488ae-a2fc-4657-a93b-159b8433c0e7" anyType_id="GROUP" pushTask_id="5e5f7c7e-9de7-4c6a-99f1-4df1af959807" fiql="name==_NO_ONE_"/>
-  <Task DTYPE="PushTask" id="0bc11a19-6454-45c2-a4e3-ceef84e5d79b" name="Export on resource-testdb2" resource_id="resource-testdb2"
-        sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
-        performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
-        unmatchingRule="ASSIGN" matchingRule="UPDATE" active="1" jobDelegate_id="PushJobDelegate"/>
+  <PushTask id="0bc11a19-6454-45c2-a4e3-ceef84e5d79b" name="Export on resource-testdb2" resource_id="resource-testdb2"
+            sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
+            performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
+            unmatchingRule="ASSIGN" matchingRule="UPDATE" active="1" jobDelegate_id="PushJobDelegate"/>
   <PushTaskAnyFilter id="41bf22fe-a014-41af-9a75-402b987eb433" anyType_id="USER" pushTask_id="0bc11a19-6454-45c2-a4e3-ceef84e5d79b" fiql="username==_NO_ONE_"/>
   <PushTaskAnyFilter id="fa983fde-795e-4c89-a6f7-1ccd80a8adeb" anyType_id="GROUP" pushTask_id="0bc11a19-6454-45c2-a4e3-ceef84e5d79b" fiql="name==_NO_ONE_"/>
-  <Task DTYPE="PushTask" id="ec674143-480a-4816-98ad-b61fa090821e" name="Export on resource-testdb2" resource_id="resource-testdb2"
-        sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
-        performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
-        unmatchingRule="IGNORE" matchingRule="DEPROVISION" active="1" jobDelegate_id="PushJobDelegate"/>
+  <PushTask id="ec674143-480a-4816-98ad-b61fa090821e" name="Export on resource-testdb2" resource_id="resource-testdb2"
+            sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
+            performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
+            unmatchingRule="IGNORE" matchingRule="DEPROVISION" active="1" jobDelegate_id="PushJobDelegate"/>
   <PushTaskAnyFilter id="e238a6dc-0b04-46cf-9bfa-be68bd9f2da0" anyType_id="USER" pushTask_id="ec674143-480a-4816-98ad-b61fa090821e" fiql="surname==Verdi"/>
   <PushTaskAnyFilter id="0eaa643e-0add-4c46-8273-539f9d6abec5" anyType_id="GROUP" pushTask_id="ec674143-480a-4816-98ad-b61fa090821e" fiql="name==_NO_ONE_"/>
-  <Task DTYPE="PushTask" id="c46edc3a-a18b-4af2-b707-f4a415507496" name="Export on resource-testdb2" resource_id="resource-testdb2"
-        sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
-        performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
-        unmatchingRule="IGNORE" matchingRule="UNASSIGN" active="1" jobDelegate_id="PushJobDelegate"/>
+  <PushTask id="c46edc3a-a18b-4af2-b707-f4a415507496" name="Export on resource-testdb2" resource_id="resource-testdb2"
+            sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
+            performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
+            unmatchingRule="IGNORE" matchingRule="UNASSIGN" active="1" jobDelegate_id="PushJobDelegate"/>
   <PushTaskAnyFilter id="335b4f11-589a-44c5-80b0-ba94892f0c62" anyType_id="USER" pushTask_id="c46edc3a-a18b-4af2-b707-f4a415507496" fiql="surname==Rossini"/>
   <PushTaskAnyFilter id="b32eecc2-aa4f-43c6-a501-a692c3e93113" anyType_id="GROUP" pushTask_id="c46edc3a-a18b-4af2-b707-f4a415507496" fiql="name==_NO_ONE_"/>
-  <Task DTYPE="PushTask" id="51318433-cce4-4f71-8f45-9534b6c9c819" name="Export on resource-testdb2" resource_id="resource-testdb2"
-        sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
-        performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
-        unmatchingRule="IGNORE" matchingRule="LINK" active="1" jobDelegate_id="PushJobDelegate"/>
+  <PushTask id="51318433-cce4-4f71-8f45-9534b6c9c819" name="Export on resource-testdb2" resource_id="resource-testdb2"
+            sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
+            performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
+            unmatchingRule="IGNORE" matchingRule="LINK" active="1" jobDelegate_id="PushJobDelegate"/>
   <PushTaskAnyFilter id="9f974a0d-87d8-4cae-9ea9-1fc245bc1dbf" anyType_id="USER" pushTask_id="51318433-cce4-4f71-8f45-9534b6c9c819" fiql="surname==Verdi"/>
   <PushTaskAnyFilter id="0dc46ba4-1270-4fa9-b3e1-79f940d4308f" anyType_id="GROUP" pushTask_id="51318433-cce4-4f71-8f45-9534b6c9c819" fiql="name==_NO_ONE_"/>
-  <Task DTYPE="PushTask" id="24b1be9c-7e3b-443a-86c9-798ebce5eaf2" name="Export on resource-testdb2" resource_id="resource-testdb2"
-        sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
-        performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
-        unmatchingRule="IGNORE" matchingRule="UNLINK" active="1" jobDelegate_id="PushJobDelegate"/>
+  <PushTask id="24b1be9c-7e3b-443a-86c9-798ebce5eaf2" name="Export on resource-testdb2" resource_id="resource-testdb2"
+            sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
+            performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
+            unmatchingRule="IGNORE" matchingRule="UNLINK" active="1" jobDelegate_id="PushJobDelegate"/>
   <PushTaskAnyFilter id="3aa3b0b8-7469-4859-89d5-476ae5915101" anyType_id="USER" pushTask_id="24b1be9c-7e3b-443a-86c9-798ebce5eaf2" fiql="surname==Verdi"/>
   <PushTaskAnyFilter id="f054810e-6842-4017-8f60-5b4031fa2c72" anyType_id="GROUP" pushTask_id="24b1be9c-7e3b-443a-86c9-798ebce5eaf2" fiql="name==_NO_ONE_"/>
-  <Task DTYPE="PushTask" id="375c7b7f-9e3a-4833-88c9-b7787b0a69f2" name="Export on resource-testdb2" resource_id="resource-testdb2"
-        sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
-        performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
-        unmatchingRule="IGNORE" matchingRule="UPDATE"  active="1" jobDelegate_id="PushJobDelegate"/>
+  <PushTask id="375c7b7f-9e3a-4833-88c9-b7787b0a69f2" name="Export on resource-testdb2" resource_id="resource-testdb2"
+            sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
+            performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
+            unmatchingRule="IGNORE" matchingRule="UPDATE"  active="1" jobDelegate_id="PushJobDelegate"/>
   <PushTaskAnyFilter id="95f047fc-1a8a-45f4-b56c-6e04d8ca5567" anyType_id="USER" pushTask_id="375c7b7f-9e3a-4833-88c9-b7787b0a69f2" fiql="surname==Verdi"/>
   <PushTaskAnyFilter id="013a4298-4b14-4f8b-9f59-191c2d53dbd8" anyType_id="GROUP" pushTask_id="375c7b7f-9e3a-4833-88c9-b7787b0a69f2" fiql="name==_NO_ONE_"/>
-  <Task DTYPE="PushTask" id="fd905ba5-9d56-4f51-83e2-859096a67b75" name="Export on resource-ldap" resource_id="resource-ldap"
-        sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
-        performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
-        unmatchingRule="ASSIGN" matchingRule="UNLINK" active="1" jobDelegate_id="PushJobDelegate"/>
+  <PushTask id="fd905ba5-9d56-4f51-83e2-859096a67b75" name="Export on resource-ldap" resource_id="resource-ldap"
+            sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
+            performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
+            unmatchingRule="ASSIGN" matchingRule="UNLINK" active="1" jobDelegate_id="PushJobDelegate"/>
   <PushTaskAnyFilter id="30842acc-f2dd-4d47-b359-20db06c30803" anyType_id="USER" pushTask_id="fd905ba5-9d56-4f51-83e2-859096a67b75" fiql="username==_NO_ONE_"/>
   <PushTaskAnyFilter id="9e4c0233-440e-4b5b-9563-11ec0f55a334" anyType_id="GROUP" pushTask_id="fd905ba5-9d56-4f51-83e2-859096a67b75" fiql="name==citizen"/>
-  <Task DTYPE="PullTask" remediation="0" id="986867e2-993b-430e-8feb-aa9abb4c1dcd" name="CSV Task (update matching; provision unmatching)" resource_id="resource-csv"
-        destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="1" syncStatus="1" pullMode="INCREMENTAL"
-        unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
+  <PullTask remediation="0" id="986867e2-993b-430e-8feb-aa9abb4c1dcd" name="CSV Task (update matching; provision unmatching)" resource_id="resource-csv"
+            destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="1" syncStatus="1" pullMode="INCREMENTAL"
+            unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
   <AnyTemplatePullTask id="8bc41ba1-cc1d-4ee0-bb43-61cd148b414f" pullTask_id="986867e2-993b-430e-8feb-aa9abb4c1dcd" anyType_id="USER"
                        template='{"_class":"org.apache.syncope.common.lib.to.UserTO","creator":null,"creationDate":null,"lastModifier":null,"lastChangeDate":null,"key":null,"type":"USER","realm":null,"status":null,"password":null,"token":null,"tokenExpireTime":null,"username":null,"lastLoginDate":null,"changePwdDate":null,"failedLogins":null,"securityQuestion":null,"securityAnswer":null,"auxClasses":[],"derAttrs":[],"virAttrs":[],"resources":["resource-testdb"],"roles":[],"dynRoles":[],"relationships":[],"memberships":[],"dynMemberships":[],"plainAttrs":[{"schema":"firstname","values":[""]},{"schema":"userId","values":["&apos;test&apos;"]},{"schema":"fullname","values":["&apos;test&apos;"]},{"schema":"surname","values":["&apos;test&apos;"]}]}'/>
   <AnyTemplatePullTask id="9af0e343-8a37-42d2-9bc7-6e2e3b103219" pullTask_id="986867e2-993b-430e-8feb-aa9abb4c1dcd" anyType_id="GROUP"
                        template='{"_class":"org.apache.syncope.common.lib.to.GroupTO","creator":null,"creationDate":null,"lastModifier":null,"lastChangeDate":null,"key":null,"type":"GROUP","realm":null,"status":null,"name":null,"userOwner":null,"groupOwner":null,"udynMembershipCond":null,"auxClasses":[],"derAttrs":[],"virAttrs":[],"resources":[],"plainAttrs":[]}'/>
-  <Task DTYPE="PullTask" remediation="0" id="feae4e57-15ca-40d9-b973-8b9015efca49" name="CSV (unlink matching; ignore unmatching)" resource_id="resource-csv"
-        destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="1" syncStatus="1" pullMode="FULL_RECONCILIATION"
-        unmatchingRule="IGNORE" matchingRule="UNLINK" active="1" jobDelegate_id="PullJobDelegate"/>
-  <Task DTYPE="PullTask" remediation="0" id="55d5e74b-497e-4bc0-9156-73abef4b9adc" name="CSV (ignore matching; assign unmatching)" resource_id="resource-csv"
-        destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="1" syncStatus="1" pullMode="FULL_RECONCILIATION"
-        unmatchingRule="ASSIGN" matchingRule="IGNORE" active="1" jobDelegate_id="PullJobDelegate"/>
-  <Task DTYPE="PropagationTask" id="0f618183-17ce-48bc-80bc-cc535f38983a" operation="CREATE"
-        objectClassName="__ACCOUNT__" resource_id="resource-testdb" anyTypeKind="USER" entityKey="1417acbe-cbf6-4277-9372-e75e04f97000"
-        propagationData='{"attributes":[{"name":"__PASSWORD__","value":[{"readOnly":false,"disposed":false,"encryptedBytes":"m9nh2US0Sa6m+cXccCq0Xw==","base64SHA1Hash":"GFJ69qfjxEOdrmt+9q+0Cw2uz60="}]},{"name":"__NAME__","value":["userId"],"nameValue":"userId"},{"name":"fullname","value":["fullname"]},{"name":"type","value":["type"]}]}'/>
-  <Task DTYPE="PullTask" remediation="0" id="30cfd653-257b-495f-8665-281281dbcb3d" name="Scripted SQL" resource_id="resource-db-scripted"
-        destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="0" syncStatus="0" pullMode="INCREMENTAL"
-        unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
+  <PullTask remediation="0" id="feae4e57-15ca-40d9-b973-8b9015efca49" name="CSV (unlink matching; ignore unmatching)" resource_id="resource-csv"
+            destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="1" syncStatus="1" pullMode="FULL_RECONCILIATION"
+            unmatchingRule="IGNORE" matchingRule="UNLINK" active="1" jobDelegate_id="PullJobDelegate"/>
+  <PullTask remediation="0" id="55d5e74b-497e-4bc0-9156-73abef4b9adc" name="CSV (ignore matching; assign unmatching)" resource_id="resource-csv"
+            destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="1" syncStatus="1" pullMode="FULL_RECONCILIATION"
+            unmatchingRule="ASSIGN" matchingRule="IGNORE" active="1" jobDelegate_id="PullJobDelegate"/>
+  <PropagationTask id="0f618183-17ce-48bc-80bc-cc535f38983a" operation="CREATE"
+                   objectClassName="__ACCOUNT__" resource_id="resource-testdb" anyTypeKind="USER" entityKey="1417acbe-cbf6-4277-9372-e75e04f97000"
+                   propagationData='{"attributes":[{"name":"__PASSWORD__","value":[{"readOnly":false,"disposed":false,"encryptedBytes":"m9nh2US0Sa6m+cXccCq0Xw==","base64SHA1Hash":"GFJ69qfjxEOdrmt+9q+0Cw2uz60="}]},{"name":"__NAME__","value":["userId"],"nameValue":"userId"},{"name":"fullname","value":["fullname"]},{"name":"type","value":["type"]}]}'/>
+  <PullTask remediation="0" id="30cfd653-257b-495f-8665-281281dbcb3d" name="Scripted SQL" resource_id="resource-db-scripted"
+            destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="0" syncStatus="0" pullMode="INCREMENTAL"
+            unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
   <Implementation id="ExpiredBatchCleanup" type="TASKJOB_DELEGATE" engine="JAVA"
                   body="org.apache.syncope.core.provisioning.java.job.ExpiredBatchCleanup"/>
-  <Task DTYPE="SchedTask" id="8ea0ea51-ce08-4fe3-a0c8-c281b31b5893" name="Expired Batch Operations Cleanup Task"  active="1"
-        jobDelegate_id="ExpiredBatchCleanup" cronExpression="0 0/5 * * * ?"/>
+  <SchedTask id="8ea0ea51-ce08-4fe3-a0c8-c281b31b5893" name="Expired Batch Operations Cleanup Task" active="1"
+             jobDelegate_id="ExpiredBatchCleanup" cronExpression="0 0/5 * * * ?"/>
 
   <MailTemplate id="requestPasswordReset"
                 textTemplate="Hi,
@@ -967,26 +937,20 @@
 
   <Notification id="e00945b5-1184-4d43-8e45-4318a8dcdfd4" active="1" recipientAttrName="email" selfAsRecipient="1" 
                 sender="admin@syncope.apache.org" subject="Password Reset request" template_id="requestPasswordReset" 
-                traceLevel="FAILURES"/> 
+                traceLevel="FAILURES" events='["[CUSTOM]:[]:[]:[requestPasswordReset]:[SUCCESS]"]'/> 
   <AnyAbout id="a328f2e6-25e9-4cc1-badf-7425d7be4b39" anyType_id="USER" notification_id="e00945b5-1184-4d43-8e45-4318a8dcdfd4" anyType_filter="token!=$null"/>
-  <Notification_events notification_id="e00945b5-1184-4d43-8e45-4318a8dcdfd4" event="[CUSTOM]:[]:[]:[requestPasswordReset]:[SUCCESS]"/>
-  <Task DTYPE="NotificationTask" id="e1e520f0-2cbd-4e11-9a89-ea58a0f957e7" notification_id="e00945b5-1184-4d43-8e45-4318a8dcdfd4"
-        sender="admin@prova.org" subject="Notification for SYNCOPE-81" executed="0"
-        textBody="NOTIFICATION-81" htmlBody="NOTIFICATION-81" traceLevel="ALL"/>
-  <NotificationTask_recipients notificationTask_id="e1e520f0-2cbd-4e11-9a89-ea58a0f957e7" address="recipient@prova.org"/>  
+  <NotificationTask id="e1e520f0-2cbd-4e11-9a89-ea58a0f957e7" notification_id="e00945b5-1184-4d43-8e45-4318a8dcdfd4"
+                    sender="admin@prova.org" subject="Notification for SYNCOPE-81" executed="0"
+                    textBody="NOTIFICATION-81" htmlBody="NOTIFICATION-81" traceLevel="ALL" recipients='["recipient@prova.org"]'/>
   
   <Notification id="bef0c250-e8a7-4848-bb63-2564fc409ce2" active="1" recipientAttrName="email" selfAsRecipient="1" 
                 sender="admin@syncope.apache.org" subject="Password Reset successful" template_id="confirmPasswordReset" 
-                traceLevel="FAILURES"/> 
-  <Notification_events notification_id="bef0c250-e8a7-4848-bb63-2564fc409ce2" event="[CUSTOM]:[]:[]:[confirmPasswordReset]:[SUCCESS]"/>
+                traceLevel="FAILURES" events='["[CUSTOM]:[]:[]:[confirmPasswordReset]:[SUCCESS]"]'/> 
 
   <Notification id="9e2b911c-25de-4c77-bcea-b86ed9451050" sender="test@syncope.apache.org" subject="Test subject" template_id="test" selfAsRecipient="0" 
-                traceLevel="FAILURES"
-                recipientsFIQL="$groups==7"
-                recipientAttrName="email" active="1"/>
+                traceLevel="FAILURES" recipientsFIQL="$groups==7" recipientAttrName="email" active="1"
+                events='["[CUSTOM]:[]:[]:[unexisting1]:[FAILURE]", "[CUSTOM]:[]:[]:[unexisting2]:[SUCCESS]"]'/>
   <AnyAbout id="2e2ee845-2abf-43c6-b543-49243a84e2f1" anyType_id="USER" notification_id="9e2b911c-25de-4c77-bcea-b86ed9451050" anyType_filter="fullname==*o*;fullname==*i*"/>
-  <Notification_events notification_id="9e2b911c-25de-4c77-bcea-b86ed9451050" event="[CUSTOM]:[]:[]:[unexisting1]:[FAILURE]"/>
-  <Notification_events notification_id="9e2b911c-25de-4c77-bcea-b86ed9451050" event="[CUSTOM]:[]:[]:[unexisting2]:[SUCCESS]"/>
 
   <ReportTemplate id="empty"/>  
   <ReportTemplate id="sample"
@@ -2102,22 +2066,7 @@
             logout="0" csrf="1"
             predicates="[{&quot;cond&quot;:null,&quot;factory&quot;:&quot;METHOD&quot;,&quot;args&quot;:&quot;GET&quot;}]"/>
 
-  <SyncopeRole id="GROUP_OWNER"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_SEARCH"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_CREATE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_UPDATE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_DELETE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPECLASS_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPE_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPECLASS_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="RELATIONSHIPTYPE_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPE_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="REALM_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_SEARCH"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_UPDATE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_DELETE"/>
+  <SyncopeRole id="GROUP_OWNER" entitlements='["USER_SEARCH","USER_READ","USER_CREATE","USER_UPDATE","USER_DELETE","ANYTYPECLASS_READ","ANYTYPE_LIST","ANYTYPECLASS_LIST","RELATIONSHIPTYPE_LIST","ANYTYPE_READ","REALM_LIST","GROUP_SEARCH","GROUP_READ","GROUP_UPDATE","GROUP_DELETE"]'/>
 
   <AuditConf id="syncope.audit.[LOGIC]:[SyncopeLogic]:[]:[isSelfRegAllowed]:[SUCCESS]" active="1"/>
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java
index 008766b..79ea9fa 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java
@@ -84,7 +84,11 @@
 import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAPlainAttrValue;
 import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAARelationship;
 import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject;
-import org.apache.syncope.core.persistence.jpa.entity.task.JPATaskExec;
+import org.apache.syncope.core.persistence.jpa.entity.task.JPANotificationTaskExec;
+import org.apache.syncope.core.persistence.jpa.entity.task.JPAPropagationTaskExec;
+import org.apache.syncope.core.persistence.jpa.entity.task.JPAPullTaskExec;
+import org.apache.syncope.core.persistence.jpa.entity.task.JPAPushTaskExec;
+import org.apache.syncope.core.persistence.jpa.entity.task.JPASchedTaskExec;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUMembership;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttr;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttrUniqueValue;
@@ -107,9 +111,11 @@
 
     protected static final Logger LOG = LoggerFactory.getLogger(XMLContentExporter.class);
 
-    protected static final Set<String> TABLE_PREFIXES_TO_BE_EXCLUDED = Stream.of(
-            "QRTZ_", "LOGGING", "NotificationTask_recipients", AuditConfDAO.AUDIT_ENTRY_TABLE, JPAReportExec.TABLE,
-            JPATaskExec.TABLE, JPAUser.TABLE, JPAUPlainAttr.TABLE, JPAUPlainAttrValue.TABLE,
+    protected static final Set<String> TABLE_PREFIXES_TO_BE_EXCLUDED = Stream.of("QRTZ_", "LOGGING",
+            AuditConfDAO.AUDIT_ENTRY_TABLE, JPAReportExec.TABLE,
+            JPAPropagationTaskExec.TABLE, JPANotificationTaskExec.TABLE, JPASchedTaskExec.TABLE,
+            JPAPushTaskExec.TABLE, JPAPullTaskExec.TABLE,
+            JPAUser.TABLE, JPAUPlainAttr.TABLE, JPAUPlainAttrValue.TABLE,
             JPAUPlainAttrUniqueValue.TABLE, JPAURelationship.TABLE, JPAUMembership.TABLE,
             JPAAnyObject.TABLE, JPAAPlainAttr.TABLE, JPAAPlainAttrValue.TABLE, JPAAPlainAttrUniqueValue.TABLE,
             JPAARelationship.TABLE, JPAAMembership.TABLE, JPAAccessToken.TABLE
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
index a1bca5e..3328a8f 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
@@ -796,7 +796,7 @@
         }
 
         query.append("SELECT DISTINCT any_id FROM ").
-                append(svs.dynrolemembership().name).append(" WHERE ").
+                append(SearchSupport.dynrolemembership().name).append(" WHERE ").
                 append("role_id=?").append(setParameter(parameters, cond.getRole())).
                 append("))");
 
@@ -853,7 +853,7 @@
         }
 
         query.append("SELECT DISTINCT any_id FROM ").
-                append(svs.dynrealmmembership().name).append(" WHERE ").
+                append(SearchSupport.dynrealmmembership().name).append(" WHERE ").
                 append("dynRealm_id=?").append(setParameter(parameters, cond.getDynRealm())).
                 append("))");
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPANotificationDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPANotificationDAO.java
index b528fc1..a56f171 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPANotificationDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPANotificationDAO.java
@@ -74,6 +74,7 @@
 
     @Override
     public Notification save(final Notification notification) {
+        ((JPANotification) notification).list2json();
         return entityManager().merge(notification);
     }
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAOIDCRPClientAppDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAOIDCRPClientAppDAO.java
index c92f123..7f9f833 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAOIDCRPClientAppDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAOIDCRPClientAppDAO.java
@@ -87,6 +87,7 @@
 
     @Override
     public OIDCRPClientApp save(final OIDCRPClientApp clientApp) {
+        ((JPAOIDCRPClientApp) clientApp).list2json();
         return entityManager().merge(clientApp);
     }
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
index a842326..03d80be 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
@@ -107,6 +107,7 @@
 
     @Override
     public Role save(final Role role) {
+        ((JPARole) role).list2json();
         return entityManager().merge(role);
     }
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPASAML2SPClientAppDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPASAML2SPClientAppDAO.java
index 2e63234..e4764ee 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPASAML2SPClientAppDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPASAML2SPClientAppDAO.java
@@ -87,6 +87,7 @@
 
     @Override
     public SAML2SPClientApp save(final SAML2SPClientApp clientApp) {
+        ((JPASAML2SPClientApp) clientApp).list2json();
         return entityManager().merge(clientApp);
     }
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java
index a938025..54e0322 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java
@@ -22,8 +22,8 @@
 import java.time.OffsetDateTime;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 import java.util.stream.Collectors;
-import javax.persistence.DiscriminatorValue;
 import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
 import javax.persistence.OneToOne;
@@ -40,46 +40,70 @@
 import org.apache.syncope.core.persistence.api.entity.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.Notification;
+import org.apache.syncope.core.persistence.api.entity.task.NotificationTask;
 import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 import org.apache.syncope.core.persistence.api.entity.task.PullTask;
 import org.apache.syncope.core.persistence.api.entity.task.PushTask;
 import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
 import org.apache.syncope.core.persistence.api.entity.task.Task;
-import org.apache.syncope.core.persistence.jpa.entity.task.AbstractTask;
 import org.apache.syncope.core.persistence.jpa.entity.task.JPANotificationTask;
 import org.apache.syncope.core.persistence.jpa.entity.task.JPAPropagationTask;
+import org.apache.syncope.core.persistence.jpa.entity.task.JPAPropagationTaskExec;
 import org.apache.syncope.core.persistence.jpa.entity.task.JPAPullTask;
 import org.apache.syncope.core.persistence.jpa.entity.task.JPAPushTask;
 import org.apache.syncope.core.persistence.jpa.entity.task.JPASchedTask;
-import org.apache.syncope.core.persistence.jpa.entity.task.JPATaskExec;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
 import org.springframework.util.ReflectionUtils;
 
-public class JPATaskDAO extends AbstractDAO<Task> implements TaskDAO {
+public class JPATaskDAO extends AbstractDAO<Task<?>> implements TaskDAO {
 
-    protected static String getEntityTableName(final TaskType type) {
+    public static TaskType getTaskType(final Task<?> task) {
+        if (task instanceof NotificationTask) {
+            return TaskType.NOTIFICATION;
+        }
+
+        if (task instanceof PropagationTask) {
+            return TaskType.PROPAGATION;
+        }
+
+        if (task instanceof PushTask) {
+            return TaskType.PUSH;
+        }
+
+        if (task instanceof PullTask) {
+            return TaskType.PULL;
+        }
+
+        if (task instanceof SchedTask) {
+            return TaskType.SCHEDULED;
+        }
+
+        return null;
+    }
+
+    public static String getEntityTableName(final TaskType type) {
         String result = null;
 
         switch (type) {
             case NOTIFICATION:
-                result = JPANotificationTask.class.getAnnotation(DiscriminatorValue.class).value();
+                result = JPANotificationTask.TABLE;
                 break;
 
             case PROPAGATION:
-                result = JPAPropagationTask.class.getAnnotation(DiscriminatorValue.class).value();
+                result = JPAPropagationTask.TABLE;
                 break;
 
             case PUSH:
-                result = JPAPushTask.class.getAnnotation(DiscriminatorValue.class).value();
+                result = JPAPushTask.TABLE;
                 break;
 
             case SCHEDULED:
-                result = JPASchedTask.class.getAnnotation(DiscriminatorValue.class).value();
+                result = JPASchedTask.TABLE;
                 break;
 
             case PULL:
-                result = JPAPullTask.class.getAnnotation(DiscriminatorValue.class).value();
+                result = JPAPullTask.TABLE;
                 break;
 
             default:
@@ -88,15 +112,8 @@
         return result;
     }
 
-    protected final RemediationDAO remediationDAO;
-
-    public JPATaskDAO(final RemediationDAO remediationDAO) {
-        this.remediationDAO = remediationDAO;
-    }
-
-    @Override
-    public Class<? extends Task> getEntityReference(final TaskType type) {
-        Class<? extends Task> result = null;
+    public static Class<? extends Task<?>> getEntityReference(final TaskType type) {
+        Class<? extends Task<?>> result = null;
 
         switch (type) {
             case NOTIFICATION:
@@ -125,12 +142,17 @@
         return result;
     }
 
+    protected final RemediationDAO remediationDAO;
+
+    public JPATaskDAO(final RemediationDAO remediationDAO) {
+        this.remediationDAO = remediationDAO;
+    }
+
     @Transactional(readOnly = true)
     @Override
     public boolean exists(final TaskType type, final String key) {
-        Query query = entityManager().createNativeQuery("SELECT id FROM Task WHERE id=? AND dtype=?");
+        Query query = entityManager().createNativeQuery("SELECT id FROM " + getEntityTableName(type) + " WHERE id=?");
         query.setParameter(1, key);
-        query.setParameter(2, getEntityTableName(type));
 
         return !query.getResultList().isEmpty();
     }
@@ -138,8 +160,27 @@
     @Transactional(readOnly = true)
     @SuppressWarnings("unchecked")
     @Override
-    public <T extends Task> T find(final String key) {
-        return (T) entityManager().find(AbstractTask.class, key);
+    public <T extends Task<T>> T find(final TaskType type, final String key) {
+        return (T) entityManager().find(getEntityReference(type), key);
+    }
+
+    @Override
+    public Optional<Task<?>> find(final String key) {
+        Task<?> task = find(TaskType.SCHEDULED, key);
+        if (task == null) {
+            task = find(TaskType.PULL, key);
+        }
+        if (task == null) {
+            task = find(TaskType.PUSH, key);
+        }
+        if (task == null) {
+            task = find(TaskType.PROPAGATION, key);
+        }
+        if (task == null) {
+            task = find(TaskType.NOTIFICATION, key);
+        }
+
+        return Optional.ofNullable(task);
     }
 
     @Override
@@ -182,7 +223,7 @@
         return query.getResultList();
     }
 
-    protected final <T extends Task> StringBuilder buildFindAllQueryJPA(final TaskType type) {
+    protected final <T extends Task<T>> StringBuilder buildFindAllQueryJPA(final TaskType type) {
         StringBuilder builder = new StringBuilder("SELECT t FROM ").
                 append(getEntityReference(type).getSimpleName()).
                 append(" t WHERE ");
@@ -201,7 +242,7 @@
 
     @Override
     @SuppressWarnings("unchecked")
-    public <T extends Task> List<T> findToExec(final TaskType type) {
+    public <T extends Task<T>> List<T> findToExec(final TaskType type) {
         StringBuilder queryString = buildFindAllQueryJPA(type).append("AND ");
 
         if (type == TaskType.NOTIFICATION) {
@@ -217,7 +258,7 @@
 
     @Transactional(readOnly = true)
     @Override
-    public <T extends Task> List<T> findAll(final TaskType type) {
+    public <T extends Task<T>> List<T> findAll(final TaskType type) {
         return findAll(type, null, null, null, null, -1, -1, List.of());
     }
 
@@ -246,58 +287,43 @@
             throw new IllegalArgumentException(type + " is not related to notifications");
         }
 
-        StringBuilder queryString = new StringBuilder("SELECT ").append(AbstractTask.TABLE).append(".*");
+        String table = getEntityTableName(type);
+        StringBuilder queryString = new StringBuilder("SELECT ").append(table).append(".*");
 
         if (orderByTaskExecInfo) {
-            queryString.append(',').append(JPATaskExec.TABLE).append(".startDate AS startDate").
-                    append(',').append(JPATaskExec.TABLE).append(".endDate AS endDate").
-                    append(',').append(JPATaskExec.TABLE).append(".status AS status").
-                    append(" FROM ").append(AbstractTask.TABLE).
-                    append(',').append(JPATaskExec.TABLE).append(',').append("(SELECT ").
-                    append(JPATaskExec.TABLE).append(".task_id, ").
-                    append("MAX(").append(JPATaskExec.TABLE).append(".startDate) AS startDate").
-                    append(" FROM ").append(JPATaskExec.TABLE).
-                    append(" GROUP BY ").append(JPATaskExec.TABLE).append(".task_id) GRP").
+            queryString.append(',').append(JPATaskExecDAO.getEntityTableName(type)).append(".startDate AS startDate").
+                    append(',').append(JPATaskExecDAO.getEntityTableName(type)).append(".endDate AS endDate").
+                    append(',').append(JPATaskExecDAO.getEntityTableName(type)).append(".status AS status").
+                    append(" FROM ").append(table).
+                    append(',').append(JPATaskExecDAO.getEntityTableName(type)).append(',').append("(SELECT ").
+                    append(JPATaskExecDAO.getEntityTableName(type)).append(".task_id, ").
+                    append("MAX(").append(JPATaskExecDAO.getEntityTableName(type)).append(".startDate) AS startDate").
+                    append(" FROM ").append(JPATaskExecDAO.getEntityTableName(type)).
+                    append(" GROUP BY ").append(JPATaskExecDAO.getEntityTableName(type)).append(".task_id) GRP").
                     append(" WHERE ").
-                    append(AbstractTask.TABLE).append(".id=").append(JPATaskExec.TABLE).append(".task_id").
-                    append(" AND ").append(AbstractTask.TABLE).append(".id=").append("GRP.task_id").
-                    append(" AND ").append(JPATaskExec.TABLE).append(".startDate=").append("GRP.startDate").
-                    append(" AND ").append(AbstractTask.TABLE).append(".DTYPE = ?1");
-        } else {
-            queryString.append(", null AS startDate, null AS endDate, null AS status FROM ").append(AbstractTask.TABLE).
-                    append(" WHERE ").append(AbstractTask.TABLE).append(".DTYPE = ?1");
-        }
-
-        queryParameters.add(getEntityTableName(type));
-        if (type == TaskType.SCHEDULED) {
-            queryString.append(" AND ").
-                    append(AbstractTask.TABLE).
-                    append(".id NOT IN (SELECT ").append(AbstractTask.TABLE).append(".id FROM ").
-                    append(AbstractTask.TABLE).append(" WHERE ").
-                    append(AbstractTask.TABLE).append(".DTYPE = ?2)").
+                    append(table).append(".id=").append(JPATaskExecDAO.getEntityTableName(type)).append(".task_id").
+                    append(" AND ").append(table).append(".id=").append("GRP.task_id").
                     append(" AND ").
-                    append(AbstractTask.TABLE).
-                    append(".id NOT IN (SELECT id FROM ").
-                    append(AbstractTask.TABLE).append(" WHERE ").
-                    append(AbstractTask.TABLE).append(".DTYPE = ?3)");
-
-            queryParameters.add(JPAPushTask.class.getAnnotation(DiscriminatorValue.class).value());
-            queryParameters.add(JPAPullTask.class.getAnnotation(DiscriminatorValue.class).value());
+                    append(JPATaskExecDAO.getEntityTableName(type)).append(".startDate=").append("GRP.startDate");
+        } else {
+            queryString.append(", null AS startDate, null AS endDate, null AS status FROM ").append(table).
+                    append(" WHERE 1=1 ");
         }
+
         queryString.append(' ');
 
         if (resource != null) {
             queryParameters.add(resource.getKey());
 
             queryString.append(" AND ").
-                    append(AbstractTask.TABLE).
+                    append(table).
                     append(".resource_id=?").append(queryParameters.size());
         }
         if (notification != null) {
             queryParameters.add(notification.getKey());
 
             queryString.append(" AND ").
-                    append(AbstractTask.TABLE).
+                    append(table).
                     append(".notification_id=?").append(queryParameters.size());
         }
         if (anyTypeKind != null && entityKey != null) {
@@ -305,10 +331,10 @@
             queryParameters.add(entityKey);
 
             queryString.append(" AND ").
-                    append(AbstractTask.TABLE).
+                    append(table).
                     append(".anyTypeKind=?").append(queryParameters.size() - 1).
                     append(" AND ").
-                    append(AbstractTask.TABLE).
+                    append(table).
                     append(".entityKey=?").append(queryParameters.size());
         }
 
@@ -316,7 +342,7 @@
     }
 
     protected String toOrderByStatement(
-            final Class<? extends Task> beanClass,
+            final Class<? extends Task<?>> beanClass,
             final List<OrderByClause> orderByClauses) {
 
         StringBuilder statement = new StringBuilder();
@@ -364,7 +390,7 @@
     }
 
     @Override
-    public <T extends Task> List<T> findAll(
+    public <T extends Task<T>> List<T> findAll(
             final TaskType type,
             final ExternalResource resource,
             final Notification notification,
@@ -403,7 +429,7 @@
                             false,
                             queryParameters)).
                     append(" AND id NOT IN ").
-                    append("(SELECT task_id AS id FROM ").append(JPATaskExec.TABLE).append(')').
+                    append("(SELECT task_id AS id FROM ").append(JPATaskExecDAO.getEntityTableName(type)).append(')').
                     append(")) T");
         } else {
             queryString.insert(0, "SELECT T.id FROM (").append(") T");
@@ -431,7 +457,7 @@
                 ? (String) ((Object[]) key)[0]
                 : ((String) key)).forEach(key -> {
 
-            T task = find(key);
+            T task = find(type, key);
             if (task == null) {
                 LOG.error("Could not find task with key {}, even if returned by native query", key);
             } else if (!result.contains(task)) {
@@ -457,8 +483,8 @@
 
         Query query = entityManager().createNativeQuery(StringUtils.replaceOnce(
                 queryString.toString(),
-                "SELECT " + AbstractTask.TABLE + ".*, null AS startDate, null AS endDate, null AS status",
-                "SELECT COUNT(" + AbstractTask.TABLE + ".id)"));
+                "SELECT " + getEntityTableName(type) + ".*, null AS startDate, null AS endDate, null AS status",
+                "SELECT COUNT(" + getEntityTableName(type) + ".id)"));
 
         for (int i = 1; i <= queryParameters.size(); i++) {
             query.setParameter(i, queryParameters.get(i - 1));
@@ -469,13 +495,16 @@
 
     @Transactional(rollbackFor = { Throwable.class })
     @Override
-    public <T extends Task> T save(final T task) {
+    public <T extends Task<T>> T save(final T task) {
+        if (task instanceof JPANotificationTask) {
+            ((JPANotificationTask) task).list2json();
+        }
         return entityManager().merge(task);
     }
 
     @Override
-    public void delete(final String id) {
-        Task task = find(id);
+    public void delete(final TaskType type, final String id) {
+        Task<?> task = find(type, id);
         if (task == null) {
             return;
         }
@@ -484,7 +513,7 @@
     }
 
     @Override
-    public void delete(final Task task) {
+    public void delete(final Task<?> task) {
         if (task instanceof PullTask) {
             remediationDAO.findByPullTask((PullTask) task).forEach(remediation -> remediation.setPullTask(null));
         }
@@ -495,7 +524,7 @@
     @Override
     public void deleteAll(final ExternalResource resource, final TaskType type) {
         findAll(type, resource, null, null, null, -1, -1, List.of()).
-                stream().map(Task::getKey).forEach(this::delete);
+                stream().map(Task<?>::getKey).forEach(key -> delete(type, key));
     }
 
     @Override
@@ -505,8 +534,11 @@
             final List<ExternalResource> externalResources) {
 
         StringBuilder queryString = new StringBuilder("SELECT t.task_id "
-                + "FROM TaskExec t INNER JOIN Task z ON t.task_id=z.id AND z.dtype='PropagationTask' "
-                + "WHERE t.enddate=(SELECT MAX(e.enddate) FROM TaskExec e WHERE e.task_id=t.task_id) ");
+                + "FROM " + JPAPropagationTaskExec.TABLE + " t "
+                + "INNER JOIN " + JPAPropagationTask.TABLE + " z "
+                + "ON t.task_id=z.id "
+                + "WHERE t.enddate=(SELECT MAX(e.enddate) FROM " + JPAPropagationTaskExec.TABLE + " e "
+                + "WHERE e.task_id=t.task_id) ");
 
         List<Object> queryParameters = new ArrayList<>();
         if (since != null) {
@@ -540,7 +572,7 @@
 
         List<PropagationTaskTO> purged = new ArrayList<>();
         raw.stream().map(Object::toString).distinct().forEach(key -> {
-            PropagationTask task = find(key);
+            PropagationTask task = find(TaskType.PROPAGATION, key);
             if (task != null) {
                 PropagationTaskTO taskTO = new PropagationTaskTO();
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskExecDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskExecDAO.java
index 6728acc..dbf6645 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskExecDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskExecDAO.java
@@ -19,19 +19,88 @@
 package org.apache.syncope.core.persistence.jpa.dao;
 
 import java.time.OffsetDateTime;
+import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
 import javax.persistence.Query;
-import javax.persistence.TypedQuery;
+import org.apache.syncope.common.lib.types.TaskType;
 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.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.entity.task.Task;
 import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
-import org.apache.syncope.core.persistence.jpa.entity.task.JPATaskExec;
+import org.apache.syncope.core.persistence.jpa.entity.task.AbstractTaskExec;
+import org.apache.syncope.core.persistence.jpa.entity.task.JPANotificationTaskExec;
+import org.apache.syncope.core.persistence.jpa.entity.task.JPAPropagationTaskExec;
+import org.apache.syncope.core.persistence.jpa.entity.task.JPAPullTaskExec;
+import org.apache.syncope.core.persistence.jpa.entity.task.JPAPushTaskExec;
+import org.apache.syncope.core.persistence.jpa.entity.task.JPASchedTaskExec;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.ReflectionUtils;
 
-public class JPATaskExecDAO extends AbstractDAO<TaskExec> implements TaskExecDAO {
+public class JPATaskExecDAO extends AbstractDAO<TaskExec<?>> implements TaskExecDAO {
+
+    public static String getEntityTableName(final TaskType type) {
+        String result = null;
+
+        switch (type) {
+            case NOTIFICATION:
+                result = JPANotificationTaskExec.TABLE;
+                break;
+
+            case PROPAGATION:
+                result = JPAPropagationTaskExec.TABLE;
+                break;
+
+            case PUSH:
+                result = JPAPushTaskExec.TABLE;
+                break;
+
+            case SCHEDULED:
+                result = JPASchedTaskExec.TABLE;
+                break;
+
+            case PULL:
+                result = JPAPullTaskExec.TABLE;
+                break;
+
+            default:
+        }
+
+        return result;
+    }
+
+    protected static Class<? extends TaskExec<?>> getEntityReference(final TaskType type) {
+        Class<? extends TaskExec<?>> result = null;
+
+        switch (type) {
+            case NOTIFICATION:
+                result = JPANotificationTaskExec.class;
+                break;
+
+            case PROPAGATION:
+                result = JPAPropagationTaskExec.class;
+                break;
+
+            case PUSH:
+                result = JPAPushTaskExec.class;
+                break;
+
+            case SCHEDULED:
+                result = JPASchedTaskExec.class;
+                break;
+
+            case PULL:
+                result = JPAPullTaskExec.class;
+                break;
+
+            default:
+        }
+
+        return result;
+    }
 
     protected final TaskDAO taskDAO;
 
@@ -39,53 +108,91 @@
         this.taskDAO = taskDAO;
     }
 
+    @SuppressWarnings("unchecked")
     @Override
-    public TaskExec find(final String key) {
-        return entityManager().find(JPATaskExec.class, key);
+    public <T extends Task<T>> TaskExec<T> find(final TaskType type, final String key) {
+        return (TaskExec<T>) entityManager().find(getEntityReference(type), key);
     }
 
     @Override
-    public List<TaskExec> findRecent(final int max) {
-        TypedQuery<TaskExec> query = entityManager().createQuery(
-                "SELECT e FROM " + JPATaskExec.class.getSimpleName() + " e "
-                + "WHERE e.end IS NOT NULL ORDER BY e.end DESC", TaskExec.class);
+    public Optional<TaskExec<?>> find(final String key) {
+        TaskExec<?> task = find(TaskType.SCHEDULED, key);
+        if (task == null) {
+            task = find(TaskType.PULL, key);
+        }
+        if (task == null) {
+            task = find(TaskType.PUSH, key);
+        }
+        if (task == null) {
+            task = find(TaskType.PROPAGATION, key);
+        }
+        if (task == null) {
+            task = find(TaskType.NOTIFICATION, key);
+        }
+
+        return Optional.ofNullable(task);
+    }
+
+    @SuppressWarnings("unchecked")
+    protected <T extends Task<T>> List<TaskExec<T>> findRecent(final TaskType type, final int max) {
+        Query query = entityManager().createQuery(
+                "SELECT e FROM " + getEntityReference(type).getSimpleName() + " e "
+                + "WHERE e.end IS NOT NULL ORDER BY e.end DESC");
         query.setMaxResults(max);
 
-        return query.getResultList();
+        List<Object> result = query.getResultList();
+        return result.stream().map(e -> (TaskExec<T>) e).collect(Collectors.toList());
     }
 
-    protected <T extends Task> TaskExec findLatest(final T task, final String field) {
-        TypedQuery<TaskExec> query = entityManager().createQuery(
-                "SELECT e FROM " + JPATaskExec.class.getSimpleName() + " e "
-                + "WHERE e.task=:task ORDER BY e." + field + " DESC", TaskExec.class);
+    @Override
+    public List<TaskExec<?>> findRecent(final int max) {
+        List<TaskExec<?>> recent = new ArrayList<>();
+
+        for (TaskType taskType : TaskType.values()) {
+            recent.addAll(findRecent(taskType, max));
+        }
+
+        return recent.stream().
+                sorted(Comparator.comparing(TaskExec<?>::getEnd).reversed()).
+                limit(max).
+                collect(Collectors.toList());
+    }
+
+    @SuppressWarnings("unchecked")
+    protected TaskExec<?> findLatest(final TaskType type, final Task<?> task, final String field) {
+        Query query = entityManager().createQuery(
+                "SELECT e FROM " + getEntityReference(type).getSimpleName() + " e "
+                + "WHERE e.task=:task ORDER BY e." + field + " DESC");
         query.setParameter("task", task);
         query.setMaxResults(1);
 
-        List<TaskExec> result = query.getResultList();
+        List<Object> result = query.getResultList();
         return result == null || result.isEmpty()
                 ? null
-                : result.iterator().next();
+                : (TaskExec<?>) result.get(0);
     }
 
     @Override
-    public <T extends Task> TaskExec findLatestStarted(final T task) {
-        return findLatest(task, "start");
+    public TaskExec<?> findLatestStarted(final TaskType type, final Task<?> task) {
+        return findLatest(type, task, "start");
     }
 
     @Override
-    public <T extends Task> TaskExec findLatestEnded(final T task) {
-        return findLatest(task, "end");
+    public TaskExec<?> findLatestEnded(final TaskType type, final Task<?> task) {
+        return findLatest(type, task, "end");
     }
 
+    @SuppressWarnings("unchecked")
     @Override
-    public <T extends Task> List<TaskExec> findAll(
-            final T task,
+    public List<TaskExec<?>> findAll(
+            final Task<?> task,
             final OffsetDateTime startedBefore,
             final OffsetDateTime startedAfter,
             final OffsetDateTime endedBefore,
             final OffsetDateTime endedAfter) {
 
-        StringBuilder queryString = new StringBuilder("SELECT e FROM ").append(JPATaskExec.class.getSimpleName()).
+        StringBuilder queryString = new StringBuilder("SELECT e FROM ").
+                append(getEntityReference(JPATaskDAO.getTaskType(task)).getSimpleName()).
                 append(" e WHERE e.task=:task ");
 
         if (startedBefore != null) {
@@ -101,7 +208,7 @@
             queryString.append(" AND e.end > :endedAfter");
         }
 
-        TypedQuery<TaskExec> query = entityManager().createQuery(queryString.toString(), TaskExec.class);
+        Query query = entityManager().createQuery(queryString.toString());
         query.setParameter("task", task);
         if (startedBefore != null) {
             query.setParameter("startedBefore", startedBefore);
@@ -116,14 +223,16 @@
             query.setParameter("endedAfter", endedAfter);
         }
 
-        return query.getResultList();
+        List<Object> result = query.getResultList();
+        return result.stream().map(e -> (TaskExec<?>) e).collect(Collectors.toList());
     }
 
     @Override
-    public int count(final String taskKey) {
+    public int count(final Task<?> task) {
         Query countQuery = entityManager().createNativeQuery(
-                "SELECT COUNT(e.id) FROM " + JPATaskExec.TABLE + " e WHERE e.task_id=?1");
-        countQuery.setParameter(1, taskKey);
+                "SELECT COUNT(e.id) FROM " + getEntityTableName(JPATaskDAO.getTaskType(task)) + " e "
+                + "WHERE e.task_id=?1");
+        countQuery.setParameter(1, task.getKey());
 
         return ((Number) countQuery.getSingleResult()).intValue();
     }
@@ -133,7 +242,7 @@
 
         orderByClauses.forEach(clause -> {
             String field = clause.getField().trim();
-            if (ReflectionUtils.findField(JPATaskExec.class, field) != null) {
+            if (ReflectionUtils.findField(AbstractTaskExec.class, field) != null) {
                 statement.append("e.").append(field).append(' ').append(clause.getDirection().name());
             }
         });
@@ -146,15 +255,17 @@
         return statement.toString();
     }
 
+    @SuppressWarnings("unchecked")
     @Override
-    public <T extends Task> List<TaskExec> findAll(
-            final T task, final int page, final int itemsPerPage, final List<OrderByClause> orderByClauses) {
+    public List<TaskExec<?>> findAll(
+            final Task<?> task, final int page, final int itemsPerPage, final List<OrderByClause> orderByClauses) {
 
-        String queryString =
-                "SELECT e FROM " + JPATaskExec.class.getSimpleName() + " e WHERE e.task=:task "
+        String queryString = "SELECT e "
+                + "FROM " + getEntityReference(JPATaskDAO.getTaskType(task)).getSimpleName() + " e "
+                + "WHERE e.task=:task "
                 + toOrderByStatement(orderByClauses);
 
-        TypedQuery<TaskExec> query = entityManager().createQuery(queryString, TaskExec.class);
+        Query query = entityManager().createQuery(queryString);
         query.setParameter("task", task);
 
         // page starts from 1, while setFirtResult() starts from 0
@@ -164,26 +275,29 @@
             query.setMaxResults(itemsPerPage);
         }
 
-        return query.getResultList();
+        List<Object> result = query.getResultList();
+        return result.stream().map(e -> (TaskExec<?>) e).collect(Collectors.toList());
     }
 
     @Transactional(rollbackFor = { Throwable.class })
     @Override
-    public TaskExec save(final TaskExec execution) {
+    public <T extends Task<T>> TaskExec<T> save(final TaskExec<T> execution) {
         return entityManager().merge(execution);
     }
 
     @Transactional(rollbackFor = { Throwable.class })
     @Override
-    public void saveAndAdd(final String taskKey, final TaskExec execution) {
-        Task task = taskDAO.find(taskKey);
+    public <T extends Task<T>> void saveAndAdd(
+            final TaskType taskType, final String taskKey, final TaskExec<T> execution) {
+
+        T task = taskDAO.find(taskType, taskKey);
         task.add(execution);
         taskDAO.save(task);
     }
 
     @Override
-    public void delete(final String key) {
-        TaskExec execution = find(key);
+    public <T extends Task<T>> void delete(final TaskType taskType, final String key) {
+        TaskExec<T> execution = find(taskType, key);
         if (execution == null) {
             return;
         }
@@ -192,7 +306,7 @@
     }
 
     @Override
-    public void delete(final TaskExec execution) {
+    public <T extends Task<T>> void delete(final TaskExec<T> execution) {
         if (execution.getTask() != null) {
             execution.getTask().getExecs().remove(execution);
         }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
index 03ad7c9..3eca458 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
@@ -407,16 +407,15 @@
             }
 
             // update user's password history with encrypted password
-            if (maxPPSpecHistory > 0 && user.getPassword() != null
+            if (maxPPSpecHistory > 0
+                    && user.getPassword() != null
                     && !user.getPasswordHistory().contains(user.getPassword())) {
 
-                user.getPasswordHistory().add(user.getPassword());
+                user.addToPasswordHistory(user.getPassword());
             }
             // keep only the last maxPPSpecHistory items in user's password history
             if (maxPPSpecHistory < user.getPasswordHistory().size()) {
-                for (int i = 0; i < user.getPasswordHistory().size() - maxPPSpecHistory; i++) {
-                    user.getPasswordHistory().remove(i);
-                }
+                user.removeOldestEntriesFromPasswordHistory(user.getPasswordHistory().size() - maxPPSpecHistory);
             }
         } catch (PersistenceException | InvalidEntityException e) {
             throw e;
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractGroupableRelatable.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractGroupableRelatable.java
index 0fd4b58..afa3303 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractGroupableRelatable.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractGroupableRelatable.java
@@ -48,59 +48,61 @@
 
     @Override
     public Optional<? extends P> getPlainAttr(final String plainSchema) {
-        return internalGetPlainAttrs().stream().filter(plainAttr
-                -> plainAttr != null && plainAttr.getSchema() != null && plainAttr.getMembership() == null
-                && plainSchema.equals(plainAttr.getSchema().getKey())).findFirst();
+        return internalGetPlainAttrs().stream().
+                filter(attr -> attr != null && attr.getSchema() != null && attr.getMembership() == null
+                && plainSchema.equals(attr.getSchema().getKey())).
+                findFirst();
     }
 
     @Override
     public Optional<? extends P> getPlainAttr(final String plainSchema, final Membership<?> membership) {
-        return internalGetPlainAttrs().stream().filter(plainAttr
-                -> plainAttr != null && plainAttr.getSchema() != null
-                && plainAttr.getMembership() != null && plainAttr.getMembership().equals(membership)
-                && plainSchema.equals(plainAttr.getSchema().getKey())).findFirst();
+        return internalGetPlainAttrs().stream().
+                filter(attr -> attr != null && attr.getSchema() != null
+                && attr.getMembership() != null && attr.getMembership().equals(membership)
+                && plainSchema.equals(attr.getSchema().getKey())).
+                findFirst();
     }
 
     @Override
     public List<? extends P> getPlainAttrs() {
-        return internalGetPlainAttrs().stream().filter(plainAttr
-                -> plainAttr != null && plainAttr.getSchema() != null && plainAttr.getMembership() == null).
+        return internalGetPlainAttrs().stream().
+                filter(attr -> attr != null && attr.getSchema() != null && attr.getMembership() == null).
                 collect(Collectors.toList());
     }
 
     @Override
     public Collection<? extends P> getPlainAttrs(final String plainSchema) {
-        return internalGetPlainAttrs().stream().filter(plainAttr
-                -> plainAttr != null && plainAttr.getSchema() != null
-                && plainSchema.equals(plainAttr.getSchema().getKey())).
+        return internalGetPlainAttrs().stream().
+                filter(attr -> attr != null && attr.getSchema() != null
+                && plainSchema.equals(attr.getSchema().getKey())).
                 collect(Collectors.toList());
     }
 
     @Override
     public Collection<? extends P> getPlainAttrs(final Membership<?> membership) {
-        return internalGetPlainAttrs().stream().filter(plainAttr
-                -> plainAttr != null && plainAttr.getSchema() != null
-                && membership.equals(plainAttr.getMembership())).
+        return internalGetPlainAttrs().stream().
+                filter(attr -> attr != null && attr.getSchema() != null && membership.equals(attr.getMembership())).
                 collect(Collectors.toList());
     }
 
     @Override
     public Optional<? extends M> getMembership(final String groupKey) {
-        return getMemberships().stream().filter(membership
-                -> groupKey != null && groupKey.equals(membership.getRightEnd().getKey())).findFirst();
+        return getMemberships().stream().
+                filter(membership -> groupKey != null && groupKey.equals(membership.getRightEnd().getKey())).
+                findFirst();
     }
 
     @Override
     public Collection<? extends REL> getRelationships(final RelationshipType relationshipType) {
-        return getRelationships().stream().filter(relationship
-                -> relationshipType != null && relationshipType.equals(relationship.getType())).
+        return getRelationships().stream().
+                filter(relationship -> relationshipType != null && relationshipType.equals(relationship.getType())).
                 collect(Collectors.toList());
     }
 
     @Override
     public Collection<? extends REL> getRelationships(final String otherEndKey) {
-        return getRelationships().stream().filter(relationship
-                -> otherEndKey != null && otherEndKey.equals(relationship.getRightEnd().getKey())).
+        return getRelationships().stream().
+                filter(relationship -> otherEndKey != null && otherEndKey.equals(relationship.getRightEnd().getKey())).
                 collect(Collectors.toList());
     }
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
index 43a46bd..1da1fb5 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity;
 
+import org.apache.syncope.common.lib.types.TaskType;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 import org.apache.syncope.core.persistence.api.entity.AccessToken;
 import org.apache.syncope.core.persistence.api.entity.AnyAbout;
@@ -140,12 +141,16 @@
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPushPolicy;
 import org.apache.syncope.core.persistence.jpa.entity.task.JPAAnyTemplatePullTask;
 import org.apache.syncope.core.persistence.jpa.entity.task.JPANotificationTask;
+import org.apache.syncope.core.persistence.jpa.entity.task.JPANotificationTaskExec;
 import org.apache.syncope.core.persistence.jpa.entity.task.JPAPropagationTask;
+import org.apache.syncope.core.persistence.jpa.entity.task.JPAPropagationTaskExec;
 import org.apache.syncope.core.persistence.jpa.entity.task.JPAPullTask;
+import org.apache.syncope.core.persistence.jpa.entity.task.JPAPullTaskExec;
 import org.apache.syncope.core.persistence.jpa.entity.task.JPAPushTask;
 import org.apache.syncope.core.persistence.jpa.entity.task.JPAPushTaskAnyFilter;
+import org.apache.syncope.core.persistence.jpa.entity.task.JPAPushTaskExec;
 import org.apache.syncope.core.persistence.jpa.entity.task.JPASchedTask;
-import org.apache.syncope.core.persistence.jpa.entity.task.JPATaskExec;
+import org.apache.syncope.core.persistence.jpa.entity.task.JPASchedTaskExec;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPADynRoleMembership;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPALAPlainAttr;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPALAPlainAttrUniqueValue;
@@ -278,8 +283,6 @@
             result = (E) new JPAPullTask();
         } else if (reference.equals(SchedTask.class)) {
             result = (E) new JPASchedTask();
-        } else if (reference.equals(TaskExec.class)) {
-            result = (E) new JPATaskExec();
         } else if (reference.equals(PushTaskAnyFilter.class)) {
             result = (E) new JPAPushTaskAnyFilter();
         } else if (reference.equals(AnyTemplatePullTask.class)) {
@@ -345,6 +348,43 @@
         return result;
     }
 
+    @SuppressWarnings("unchecked")
+    @Override
+    public <E extends TaskExec<?>> E newTaskExec(final TaskType taskType) {
+        E result;
+
+        switch (taskType) {
+            case NOTIFICATION:
+                result = (E) new JPANotificationTaskExec();
+                break;
+
+            case PROPAGATION:
+                result = (E) new JPAPropagationTaskExec();
+                break;
+
+            case PULL:
+                result = (E) new JPAPullTaskExec();
+                break;
+
+            case PUSH:
+                result = (E) new JPAPushTaskExec();
+                break;
+
+            case SCHEDULED:
+                result = (E) new JPASchedTaskExec();
+                break;
+
+            default:
+                result = null;
+        }
+
+        if (result instanceof AbstractGeneratedKeyEntity) {
+            ((AbstractGeneratedKeyEntity) result).setKey(SecureRandomUtils.generateRandomUUID().toString());
+        }
+
+        return result;
+    }
+
     @Override
     public ConnPoolConf newConnPoolConf() {
         return new JPAConnPoolConf();
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPANotification.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPANotification.java
index 087375e..be5dde7 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPANotification.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPANotification.java
@@ -18,21 +18,26 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity;
 
+import com.fasterxml.jackson.core.type.TypeReference;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
 import javax.persistence.CascadeType;
-import javax.persistence.CollectionTable;
-import javax.persistence.Column;
-import javax.persistence.ElementCollection;
 import javax.persistence.Entity;
 import javax.persistence.EnumType;
 import javax.persistence.Enumerated;
 import javax.persistence.FetchType;
 import javax.persistence.JoinColumn;
+import javax.persistence.Lob;
 import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
+import javax.persistence.PostLoad;
+import javax.persistence.PostPersist;
+import javax.persistence.PostUpdate;
+import javax.persistence.PrePersist;
+import javax.persistence.PreUpdate;
 import javax.persistence.Table;
+import javax.persistence.Transient;
 import javax.validation.constraints.NotNull;
 import org.apache.syncope.common.lib.types.IdRepoImplementationType;
 import org.apache.syncope.common.lib.types.TraceLevel;
@@ -41,6 +46,7 @@
 import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.MailTemplate;
 import org.apache.syncope.core.persistence.api.entity.Notification;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 
 @Entity
 @Table(name = JPANotification.TABLE)
@@ -50,24 +56,22 @@
 
     public static final String TABLE = "Notification";
 
-    @ElementCollection(fetch = FetchType.EAGER)
-    @Column(name = "event")
-    @CollectionTable(name = "Notification_events",
-            joinColumns =
-            @JoinColumn(name = "notification_id", referencedColumnName = "id"))
-    private List<String> events = new ArrayList<>();
+    @Lob
+    private String events;
+
+    @Transient
+    private List<String> eventsList = new ArrayList<>();
 
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "notification")
     private List<JPAAnyAbout> abouts = new ArrayList<>();
 
     private String recipientsFIQL;
 
-    @ElementCollection(fetch = FetchType.EAGER)
-    @CollectionTable(name = "Notification_staticRecipients",
-            joinColumns =
-            @JoinColumn(name = "notification_id", referencedColumnName = "id"))
-    @Column(name = "staticRecipients")
-    private List<String> staticRecipients = new ArrayList<>();
+    @Lob
+    private String staticRecipients;
+
+    @Transient
+    private List<String> staticRecipientsList = new ArrayList<>();
 
     @NotNull
     private String recipientAttrName;
@@ -129,7 +133,7 @@
 
     @Override
     public List<String> getEvents() {
-        return events;
+        return eventsList;
     }
 
     @Override
@@ -150,7 +154,7 @@
 
     @Override
     public List<String> getStaticRecipients() {
-        return staticRecipients;
+        return staticRecipientsList;
     }
 
     @Override
@@ -214,4 +218,39 @@
     public void setActive(final boolean active) {
         this.active = active;
     }
+
+    protected void json2list(final boolean clearFirst) {
+        if (clearFirst) {
+            getEvents().clear();
+            getStaticRecipients().clear();
+        }
+        if (events != null) {
+            getEvents().addAll(
+                    POJOHelper.deserialize(events, new TypeReference<List<String>>() {
+                    }));
+        }
+        if (staticRecipients != null) {
+            getStaticRecipients().addAll(
+                    POJOHelper.deserialize(staticRecipients, new TypeReference<List<String>>() {
+                    }));
+        }
+    }
+
+    @PostLoad
+    public void postLoad() {
+        json2list(false);
+    }
+
+    @PostPersist
+    @PostUpdate
+    public void postSave() {
+        json2list(true);
+    }
+
+    @PrePersist
+    @PreUpdate
+    public void list2json() {
+        events = POJOHelper.serialize(getEvents());
+        staticRecipients = POJOHelper.serialize(getStaticRecipients());
+    }
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
index e721436..ce77310 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity;
 
+import com.fasterxml.jackson.core.type.TypeReference;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
@@ -25,9 +26,6 @@
 import java.util.stream.Collectors;
 import javax.persistence.Cacheable;
 import javax.persistence.CascadeType;
-import javax.persistence.CollectionTable;
-import javax.persistence.Column;
-import javax.persistence.ElementCollection;
 import javax.persistence.Entity;
 import javax.persistence.FetchType;
 import javax.persistence.JoinColumn;
@@ -35,7 +33,13 @@
 import javax.persistence.Lob;
 import javax.persistence.ManyToMany;
 import javax.persistence.OneToOne;
+import javax.persistence.PostLoad;
+import javax.persistence.PostPersist;
+import javax.persistence.PostUpdate;
+import javax.persistence.PrePersist;
+import javax.persistence.PreUpdate;
 import javax.persistence.Table;
+import javax.persistence.Transient;
 import javax.persistence.UniqueConstraint;
 import javax.validation.Valid;
 import org.apache.syncope.core.persistence.api.entity.Application;
@@ -46,6 +50,7 @@
 import org.apache.syncope.core.persistence.api.entity.user.DynRoleMembership;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPADynRoleMembership;
 import org.apache.syncope.core.persistence.jpa.validation.entity.RoleCheck;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 
 @Entity
 @Table(name = JPARole.TABLE)
@@ -57,12 +62,11 @@
 
     public static final String TABLE = "SyncopeRole";
 
-    @ElementCollection(fetch = FetchType.EAGER)
-    @Column(name = "entitlement")
-    @CollectionTable(name = "SyncopeRole_entitlements",
-            joinColumns =
-            @JoinColumn(name = "role_id", referencedColumnName = "id"))
-    private Set<String> entitlements = new HashSet<>();
+    @Lob
+    private String entitlements;
+
+    @Transient
+    private Set<String> entitlementsSet = new HashSet<>();
 
     @ManyToMany(fetch = FetchType.EAGER)
     @JoinTable(joinColumns =
@@ -103,7 +107,7 @@
 
     @Override
     public Set<String> getEntitlements() {
-        return entitlements;
+        return entitlementsSet;
     }
 
     @Override
@@ -167,4 +171,31 @@
         return privileges;
     }
 
+    protected void json2list(final boolean clearFirst) {
+        if (clearFirst) {
+            getEntitlements().clear();
+        }
+        if (entitlements != null) {
+            getEntitlements().addAll(
+                    POJOHelper.deserialize(entitlements, new TypeReference<Set<String>>() {
+                    }));
+        }
+    }
+
+    @PostLoad
+    public void postLoad() {
+        json2list(false);
+    }
+
+    @PostPersist
+    @PostUpdate
+    public void postSave() {
+        json2list(true);
+    }
+
+    @PrePersist
+    @PreUpdate
+    public void list2json() {
+        entitlements = POJOHelper.serialize(getEntitlements());
+    }
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCRPClientApp.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCRPClientApp.java
index 35e8d31..3ca91b1 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCRPClientApp.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCRPClientApp.java
@@ -18,21 +18,26 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity.am;
 
+import com.fasterxml.jackson.core.type.TypeReference;
 import java.util.HashSet;
 import java.util.Set;
-import javax.persistence.CollectionTable;
 import javax.persistence.Column;
-import javax.persistence.ElementCollection;
 import javax.persistence.Entity;
 import javax.persistence.EnumType;
 import javax.persistence.Enumerated;
-import javax.persistence.FetchType;
-import javax.persistence.JoinColumn;
+import javax.persistence.Lob;
+import javax.persistence.PostLoad;
+import javax.persistence.PostPersist;
+import javax.persistence.PostUpdate;
+import javax.persistence.PrePersist;
+import javax.persistence.PreUpdate;
 import javax.persistence.Table;
+import javax.persistence.Transient;
 import org.apache.syncope.common.lib.types.OIDCGrantType;
 import org.apache.syncope.common.lib.types.OIDCResponseType;
 import org.apache.syncope.common.lib.types.OIDCSubjectType;
 import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 
 @Entity
 @Table(name = JPAOIDCRPClientApp.TABLE)
@@ -56,34 +61,29 @@
     @Enumerated(EnumType.STRING)
     private OIDCSubjectType subjectType;
 
-    @ElementCollection(fetch = FetchType.EAGER)
-    @Column(name = "redirectUri")
-    @CollectionTable(name = "OIDCRPClientApp_RedirectUris",
-            joinColumns =
-            @JoinColumn(name = "client_id", referencedColumnName = "id"))
-    private Set<String> redirectUris = new HashSet<>();
+    @Lob
+    private String redirectUris;
 
-    @ElementCollection(fetch = FetchType.EAGER)
-    @Enumerated(EnumType.STRING)
-    @Column(name = "supportedGrantType")
-    @CollectionTable(name = "OIDCRPClientApp_SuppGrantTypes",
-            joinColumns =
-            @JoinColumn(name = "client_id", referencedColumnName = "id"))
-    private Set<OIDCGrantType> supportedGrantTypes = new HashSet<>();
+    @Transient
+    private Set<String> redirectUrisSet = new HashSet<>();
 
-    @ElementCollection(fetch = FetchType.EAGER)
-    @Enumerated(EnumType.STRING)
-    @Column(name = "supportedResponseType")
-    @CollectionTable(name = "OIDCRPClientApp_SuppResTypes",
-            joinColumns =
-            @JoinColumn(name = "client_id", referencedColumnName = "id"))
-    private Set<OIDCResponseType> supportedResponseTypes = new HashSet<>();
+    @Lob
+    private String supportedGrantTypes;
+
+    @Transient
+    private Set<OIDCGrantType> supportedGrantTypesSet = new HashSet<>();
+
+    @Lob
+    private String supportedResponseTypes;
+
+    @Transient
+    private Set<OIDCResponseType> supportedResponseTypesSet = new HashSet<>();
 
     private String logoutUri;
 
     @Override
     public Set<String> getRedirectUris() {
-        return redirectUris;
+        return redirectUrisSet;
     }
 
     @Override
@@ -148,12 +148,12 @@
 
     @Override
     public Set<OIDCGrantType> getSupportedGrantTypes() {
-        return supportedGrantTypes;
+        return supportedGrantTypesSet;
     }
 
     @Override
     public Set<OIDCResponseType> getSupportedResponseTypes() {
-        return supportedResponseTypes;
+        return supportedResponseTypesSet;
     }
 
     @Override
@@ -165,4 +165,46 @@
     public void setLogoutUri(final String logoutUri) {
         this.logoutUri = logoutUri;
     }
+
+    protected void json2list(final boolean clearFirst) {
+        if (clearFirst) {
+            getRedirectUris().clear();
+            getSupportedGrantTypes().clear();
+            getSupportedResponseTypes().clear();
+        }
+        if (redirectUris != null) {
+            getRedirectUris().addAll(
+                    POJOHelper.deserialize(redirectUris, new TypeReference<Set<String>>() {
+                    }));
+        }
+        if (supportedGrantTypes != null) {
+            getSupportedGrantTypes().addAll(
+                    POJOHelper.deserialize(supportedGrantTypes, new TypeReference<Set<OIDCGrantType>>() {
+                    }));
+        }
+        if (supportedResponseTypes != null) {
+            getSupportedResponseTypes().addAll(
+                    POJOHelper.deserialize(supportedResponseTypes, new TypeReference<Set<OIDCResponseType>>() {
+                    }));
+        }
+    }
+
+    @PostLoad
+    public void postLoad() {
+        json2list(false);
+    }
+
+    @PostPersist
+    @PostUpdate
+    public void postSave() {
+        json2list(true);
+    }
+
+    @PrePersist
+    @PreUpdate
+    public void list2json() {
+        redirectUris = POJOHelper.serialize(getRedirectUris());
+        supportedGrantTypes = POJOHelper.serialize(getSupportedGrantTypes());
+        supportedResponseTypes = POJOHelper.serialize(getSupportedResponseTypes());
+    }
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPASAML2SPClientApp.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPASAML2SPClientApp.java
index 1291671..555f5d0 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPASAML2SPClientApp.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPASAML2SPClientApp.java
@@ -18,20 +18,25 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity.am;
 
+import com.fasterxml.jackson.core.type.TypeReference;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-import javax.persistence.CollectionTable;
 import javax.persistence.Column;
-import javax.persistence.ElementCollection;
 import javax.persistence.Entity;
-import javax.persistence.FetchType;
-import javax.persistence.JoinColumn;
+import javax.persistence.Lob;
+import javax.persistence.PostLoad;
+import javax.persistence.PostPersist;
+import javax.persistence.PostUpdate;
+import javax.persistence.PrePersist;
+import javax.persistence.PreUpdate;
 import javax.persistence.Table;
+import javax.persistence.Transient;
 import org.apache.syncope.common.lib.types.SAML2SPNameId;
 import org.apache.syncope.common.lib.types.XmlSecAlgorithm;
 import org.apache.syncope.core.persistence.api.entity.am.SAML2SPClientApp;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 
 @Entity
 @Table(name = JPASAML2SPClientApp.TABLE)
@@ -66,57 +71,56 @@
 
     private String nameIdQualifier;
 
-    @ElementCollection(fetch = FetchType.EAGER)
-    @Column(name = "assertionAudience")
-    @CollectionTable(name = "SAML2SPClientApp_AssAud",
-            joinColumns =
-            @JoinColumn(name = "client_app_id", referencedColumnName = "id"))
-    private Set<String> assertionAudiences = new HashSet<>();
+    @Lob
+    private String assertionAudiences;
+
+    @Transient
+    private Set<String> assertionAudiencesSet = new HashSet<>();
 
     @Column(name = "spNameIdQualifier")
     private String serviceProviderNameIdQualifier;
 
-    @ElementCollection(fetch = FetchType.EAGER)
-    @Column(name = "sigAlg")
-    @CollectionTable(name = "SAML2SPClientApp_SigAlgs",
-            joinColumns =
-            @JoinColumn(name = "client_app_id", referencedColumnName = "id"))
-    private List<XmlSecAlgorithm> signingSignatureAlgorithms = new ArrayList<>();
+    @Column(name = "sigAlgs")
+    @Lob
+    private String signingSignatureAlgorithms;
 
-    @ElementCollection(fetch = FetchType.EAGER)
+    @Transient
+    private List<XmlSecAlgorithm> signingSignatureAlgorithmsList = new ArrayList<>();
+
     @Column(name = "sigRefDigestMethod")
-    @CollectionTable(name = "SAML2SPClientApp_SigRefDigAlgs",
-            joinColumns =
-            @JoinColumn(name = "client_app_id", referencedColumnName = "id"))
-    private List<XmlSecAlgorithm> signingSignatureReferenceDigestMethods = new ArrayList<>();
+    @Lob
+    private String signingSignatureReferenceDigestMethods;
 
-    @ElementCollection(fetch = FetchType.EAGER)
+    @Transient
+    private List<XmlSecAlgorithm> signingSignatureReferenceDigestMethodsList = new ArrayList<>();
+
     @Column(name = "encDataAlg")
-    @CollectionTable(name = "SAML2SPClientApp_EncDataAlgs",
-            joinColumns =
-            @JoinColumn(name = "client_app_id", referencedColumnName = "id"))
-    private List<XmlSecAlgorithm> encryptionDataAlgorithms = new ArrayList<>();
+    @Lob
+    private String encryptionDataAlgorithms;
 
-    @ElementCollection(fetch = FetchType.EAGER)
+    @Transient
+    private List<XmlSecAlgorithm> encryptionDataAlgorithmsList = new ArrayList<>();
+
     @Column(name = "encKeyAlg")
-    @CollectionTable(name = "SAML2SPClientApp_EncKeyAlgs",
-            joinColumns =
-            @JoinColumn(name = "client_app_id", referencedColumnName = "id"))
-    private List<XmlSecAlgorithm> encryptionKeyAlgorithms = new ArrayList<>();
+    @Lob
+    private String encryptionKeyAlgorithms;
 
-    @ElementCollection(fetch = FetchType.EAGER)
+    @Transient
+    private List<XmlSecAlgorithm> encryptionKeyAlgorithmsList = new ArrayList<>();
+
     @Column(name = "sigBlAlg")
-    @CollectionTable(name = "SAML2SPClientApp_BlSigAlgs",
-            joinColumns =
-            @JoinColumn(name = "client_app_id", referencedColumnName = "id"))
-    private List<XmlSecAlgorithm> signingSignatureBlackListedAlgorithms = new ArrayList<>();
+    @Lob
+    private String signingSignatureBlackListedAlgorithms;
 
-    @ElementCollection(fetch = FetchType.EAGER)
+    @Transient
+    private List<XmlSecAlgorithm> signingSignatureBlackListedAlgorithmsList = new ArrayList<>();
+
     @Column(name = "encBlAlg")
-    @CollectionTable(name = "SAML2SPClientApp_BlEncAlgs",
-            joinColumns =
-            @JoinColumn(name = "client_app_id", referencedColumnName = "id"))
-    private List<XmlSecAlgorithm> encryptionBlackListedAlgorithms = new ArrayList<>();
+    @Lob
+    private String encryptionBlackListedAlgorithms;
+
+    @Transient
+    private List<XmlSecAlgorithm> encryptionBlackListedAlgorithmsList = new ArrayList<>();
 
     @Override
     public String getEntityId() {
@@ -230,7 +234,7 @@
 
     @Override
     public Set<String> getAssertionAudiences() {
-        return assertionAudiences;
+        return assertionAudiencesSet;
     }
 
     @Override
@@ -245,31 +249,108 @@
 
     @Override
     public List<XmlSecAlgorithm> getSigningSignatureAlgorithms() {
-        return signingSignatureAlgorithms;
+        return signingSignatureAlgorithmsList;
     }
 
     @Override
     public List<XmlSecAlgorithm> getSigningSignatureReferenceDigestMethods() {
-        return signingSignatureReferenceDigestMethods;
+        return signingSignatureReferenceDigestMethodsList;
     }
 
     @Override
     public List<XmlSecAlgorithm> getEncryptionDataAlgorithms() {
-        return encryptionDataAlgorithms;
+        return encryptionDataAlgorithmsList;
     }
 
     @Override
     public List<XmlSecAlgorithm> getEncryptionKeyAlgorithms() {
-        return encryptionKeyAlgorithms;
+        return encryptionKeyAlgorithmsList;
     }
 
     @Override
     public List<XmlSecAlgorithm> getSigningSignatureBlackListedAlgorithms() {
-        return signingSignatureBlackListedAlgorithms;
+        return signingSignatureBlackListedAlgorithmsList;
     }
 
     @Override
     public List<XmlSecAlgorithm> getEncryptionBlackListedAlgorithms() {
-        return encryptionBlackListedAlgorithms;
+        return encryptionBlackListedAlgorithmsList;
+    }
+
+    protected void json2list(final boolean clearFirst) {
+        if (clearFirst) {
+            getAssertionAudiences().clear();
+            getSigningSignatureAlgorithms().clear();
+            getSigningSignatureReferenceDigestMethods().clear();
+            getEncryptionDataAlgorithms().clear();
+            getEncryptionKeyAlgorithms().clear();
+            getSigningSignatureBlackListedAlgorithms().clear();
+            getEncryptionBlackListedAlgorithms().clear();
+        }
+        if (assertionAudiences != null) {
+            getAssertionAudiences().addAll(
+                    POJOHelper.deserialize(assertionAudiences,
+                            new TypeReference<Set<String>>() {
+                    }));
+        }
+        if (signingSignatureAlgorithms != null) {
+            getSigningSignatureAlgorithms().addAll(
+                    POJOHelper.deserialize(signingSignatureAlgorithms,
+                            new TypeReference<List<XmlSecAlgorithm>>() {
+                    }));
+        }
+        if (signingSignatureReferenceDigestMethods != null) {
+            getSigningSignatureReferenceDigestMethods().addAll(
+                    POJOHelper.deserialize(signingSignatureReferenceDigestMethods,
+                            new TypeReference<List<XmlSecAlgorithm>>() {
+                    }));
+        }
+        if (encryptionDataAlgorithms != null) {
+            getEncryptionDataAlgorithms().addAll(
+                    POJOHelper.deserialize(encryptionDataAlgorithms,
+                            new TypeReference<List<XmlSecAlgorithm>>() {
+                    }));
+        }
+        if (encryptionKeyAlgorithms != null) {
+            getEncryptionKeyAlgorithms().addAll(
+                    POJOHelper.deserialize(encryptionKeyAlgorithms,
+                            new TypeReference<List<XmlSecAlgorithm>>() {
+                    }));
+        }
+        if (signingSignatureBlackListedAlgorithms != null) {
+            getSigningSignatureBlackListedAlgorithms().addAll(
+                    POJOHelper.deserialize(signingSignatureBlackListedAlgorithms,
+                            new TypeReference<List<XmlSecAlgorithm>>() {
+                    }));
+        }
+        if (encryptionBlackListedAlgorithms != null) {
+            getEncryptionBlackListedAlgorithms().addAll(
+                    POJOHelper.deserialize(encryptionBlackListedAlgorithms,
+                            new TypeReference<List<XmlSecAlgorithm>>() {
+                    }));
+        }
+    }
+
+    @PostLoad
+    public void postLoad() {
+        json2list(false);
+    }
+
+    @PostPersist
+    @PostUpdate
+    public void postSave() {
+        json2list(true);
+    }
+
+    @PrePersist
+    @PreUpdate
+    public void list2json() {
+        assertionAudiences = POJOHelper.serialize(getAssertionAudiences());
+        signingSignatureAlgorithms = POJOHelper.serialize(getSigningSignatureAlgorithms());
+        signingSignatureReferenceDigestMethods = POJOHelper.serialize(getSigningSignatureReferenceDigestMethods());
+        encryptionDataAlgorithms = POJOHelper.serialize(getEncryptionDataAlgorithms());
+        encryptionKeyAlgorithms = POJOHelper.serialize(getEncryptionKeyAlgorithms());
+        signingSignatureBlackListedAlgorithms = POJOHelper.serialize(getSigningSignatureBlackListedAlgorithms());
+        encryptionBlackListedAlgorithms = POJOHelper.serialize(getEncryptionBlackListedAlgorithms());
     }
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractProvisioningTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractProvisioningTask.java
index c2cd074..f281612 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractProvisioningTask.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractProvisioningTask.java
@@ -27,12 +27,14 @@
 import org.apache.syncope.common.lib.types.UnmatchingRule;
 import org.apache.syncope.core.persistence.api.entity.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask;
+import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
 import org.apache.syncope.core.persistence.jpa.entity.JPAExternalResource;
 import org.apache.syncope.core.persistence.jpa.validation.entity.ProvisioningTaskCheck;
 
 @MappedSuperclass
 @ProvisioningTaskCheck
-public abstract class AbstractProvisioningTask extends JPASchedTask implements ProvisioningTask {
+public abstract class AbstractProvisioningTask<T extends SchedTask>
+        extends JPASchedTask implements ProvisioningTask<T> {
 
     private static final long serialVersionUID = -4141057723006682562L;
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractTask.java
index e2cd682..3b17cc5 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractTask.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractTask.java
@@ -18,41 +18,32 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity.task;
 
-import java.util.ArrayList;
 import java.util.List;
-import javax.persistence.CascadeType;
-import javax.persistence.DiscriminatorColumn;
-import javax.persistence.Entity;
-import javax.persistence.Inheritance;
-import javax.persistence.InheritanceType;
-import javax.persistence.OneToMany;
-import javax.persistence.Table;
+import javax.persistence.MappedSuperclass;
 import org.apache.syncope.core.persistence.api.entity.task.Task;
 import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 import org.apache.syncope.core.persistence.jpa.entity.AbstractGeneratedKeyEntity;
 
-@Entity
-@Table(name = AbstractTask.TABLE)
-@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
-@DiscriminatorColumn(name = "DTYPE")
-public abstract class AbstractTask extends AbstractGeneratedKeyEntity implements Task {
+@MappedSuperclass
+public abstract class AbstractTask<T extends Task<T>> extends AbstractGeneratedKeyEntity implements Task<T> {
 
     private static final long serialVersionUID = 5837401178128177511L;
 
-    public static final String TABLE = "Task";
+    protected abstract List<TaskExec<T>> executions();
 
-    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "task")
-    private List<JPATaskExec> executions = new ArrayList<>();
+    protected abstract Class<? extends TaskExec<T>> executionClass();
 
     @Override
-    public boolean add(final TaskExec exec) {
-        checkType(exec, JPATaskExec.class);
-        return exec != null && !executions.contains((JPATaskExec) exec) && executions.add((JPATaskExec) exec);
+    public boolean add(final TaskExec<T> exec) {
+        Class<? extends TaskExec<T>> clazz = executionClass();
+        checkType(exec, clazz);
+        return exec != null
+                && !executions().contains(clazz.cast(exec))
+                && executions().add(clazz.cast(exec));
     }
 
     @Override
-    public List<? extends TaskExec> getExecs() {
-        return executions;
+    public List<? extends TaskExec<T>> getExecs() {
+        return executions();
     }
-
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATaskExec.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractTaskExec.java
similarity index 69%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATaskExec.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractTaskExec.java
index 533ae33..be18a7c 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATaskExec.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractTaskExec.java
@@ -18,50 +18,21 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity.task;
 
-import javax.persistence.Entity;
-import javax.persistence.ManyToOne;
-import javax.persistence.Table;
 import org.apache.syncope.core.persistence.api.entity.task.Task;
 import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 import org.apache.syncope.core.persistence.jpa.entity.AbstractExec;
 
-/**
- * An execution (with result) of a Task.
- *
- * @see AbstractTask
- */
-@Entity
-@Table(name = JPATaskExec.TABLE)
-public class JPATaskExec extends AbstractExec implements TaskExec {
+public abstract class AbstractTaskExec<T extends Task<T>> extends AbstractExec implements TaskExec<T> {
 
     private static final long serialVersionUID = 1909033231464074554L;
 
-    public static final String TABLE = "TaskExec";
-
-    /**
-     * The referred task.
-     */
-    @ManyToOne(optional = false)
-    private AbstractTask task;
-
-    @Override
-    public Task getTask() {
-        return task;
-    }
-
-    @Override
-    public void setTask(final Task task) {
-        checkType(task, AbstractTask.class);
-        this.task = (AbstractTask) task;
-    }
-
     @Override
     public String toString() {
         return new StringBuilder(getClass().getSimpleName()).append('{').
                 append("id=").append(getKey()).append(", ").
                 append("start=").append(start).append(", ").
                 append("end=").append(end).append(", ").
-                append("task=").append(task).append(", ").
+                append("task=").append(getTask()).append(", ").
                 append("status=").append(status).append(", ").
                 append("message=").append(message).
                 append('}').
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPANotificationTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPANotificationTask.java
index c25c7bb..aeb7c5f 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPANotificationTask.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPANotificationTask.java
@@ -18,32 +18,42 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity.task;
 
+import com.fasterxml.jackson.core.type.TypeReference;
+import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
-import javax.persistence.CollectionTable;
-import javax.persistence.Column;
-import javax.persistence.DiscriminatorValue;
-import javax.persistence.ElementCollection;
+import javax.persistence.CascadeType;
 import javax.persistence.Entity;
 import javax.persistence.EnumType;
 import javax.persistence.Enumerated;
-import javax.persistence.FetchType;
-import javax.persistence.JoinColumn;
 import javax.persistence.Lob;
 import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.PostLoad;
+import javax.persistence.PostPersist;
+import javax.persistence.PostUpdate;
+import javax.persistence.PrePersist;
+import javax.persistence.PreUpdate;
+import javax.persistence.Table;
+import javax.persistence.Transient;
 import javax.validation.constraints.NotNull;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.TraceLevel;
 import org.apache.syncope.core.persistence.api.entity.Notification;
 import org.apache.syncope.core.persistence.api.entity.task.NotificationTask;
+import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 import org.apache.syncope.core.persistence.jpa.entity.JPANotification;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 
 @Entity
-@DiscriminatorValue("NotificationTask")
-public class JPANotificationTask extends AbstractTask implements NotificationTask {
+@Table(name = JPANotificationTask.TABLE)
+public class JPANotificationTask extends AbstractTask<NotificationTask> implements NotificationTask {
 
     private static final long serialVersionUID = 95731573485279180L;
 
+    public static final String TABLE = "NotificationTask";
+
     @NotNull
     @ManyToOne
     private JPANotification notification;
@@ -53,12 +63,15 @@
 
     private String entityKey;
 
-    @ElementCollection(fetch = FetchType.EAGER)
-    @Column(name = "address")
-    @CollectionTable(name = "NotificationTask_recipients",
-            joinColumns =
-            @JoinColumn(name = "notificationTask_id", referencedColumnName = "id"))
-    private Set<String> recipients = new HashSet<>();
+    @Lob
+    private String recipients;
+
+    @Transient
+    private Set<String> recipientsSet = new HashSet<>();
+
+    @OneToMany(targetEntity = JPANotificationTaskExec.class,
+            cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "task")
+    private List<TaskExec<NotificationTask>> executions = new ArrayList<>();
 
     @NotNull
     private String sender;
@@ -114,7 +127,7 @@
 
     @Override
     public Set<String> getRecipients() {
-        return recipients;
+        return recipientsSet;
     }
 
     @Override
@@ -176,4 +189,42 @@
     public void setTraceLevel(final TraceLevel traceLevel) {
         this.traceLevel = traceLevel;
     }
+
+    @Override
+    protected Class<? extends TaskExec<NotificationTask>> executionClass() {
+        return JPANotificationTaskExec.class;
+    }
+
+    @Override
+    protected List<TaskExec<NotificationTask>> executions() {
+        return executions;
+    }
+
+    protected void json2list(final boolean clearFirst) {
+        if (clearFirst) {
+            getRecipients().clear();
+        }
+        if (recipients != null) {
+            getRecipients().addAll(
+                    POJOHelper.deserialize(recipients, new TypeReference<List<String>>() {
+                    }));
+        }
+    }
+
+    @PostLoad
+    public void postLoad() {
+        json2list(false);
+    }
+
+    @PostPersist
+    @PostUpdate
+    public void postSave() {
+        json2list(true);
+    }
+
+    @PrePersist
+    @PreUpdate
+    public void list2json() {
+        recipients = POJOHelper.serialize(getRecipients());
+    }
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPANotificationTaskExec.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPANotificationTaskExec.java
new file mode 100644
index 0000000..f37917c
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPANotificationTaskExec.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.persistence.jpa.entity.task;
+
+import javax.persistence.Entity;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import org.apache.syncope.core.persistence.api.entity.task.NotificationTask;
+import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
+
+@Entity
+@Table(name = JPANotificationTaskExec.TABLE)
+public class JPANotificationTaskExec extends AbstractTaskExec<NotificationTask> implements TaskExec<NotificationTask> {
+
+    private static final long serialVersionUID = 1909033231464074554L;
+
+    public static final String TABLE = "NotificationTaskExec";
+
+    @ManyToOne(optional = false)
+    private JPANotificationTask task;
+
+    @Override
+    public NotificationTask getTask() {
+        return task;
+    }
+
+    @Override
+    public void setTask(final NotificationTask task) {
+        checkType(task, NotificationTask.class);
+        this.task = (JPANotificationTask) task;
+    }
+}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPropagationTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPropagationTask.java
index 6d30db5..67cc779 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPropagationTask.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPropagationTask.java
@@ -18,18 +18,23 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity.task;
 
-import javax.persistence.DiscriminatorValue;
+import java.util.ArrayList;
+import java.util.List;
+import javax.persistence.CascadeType;
 import javax.persistence.Entity;
 import javax.persistence.EnumType;
 import javax.persistence.Enumerated;
 import javax.persistence.Lob;
 import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.core.persistence.api.entity.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.task.PropagationData;
 import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
+import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 import org.apache.syncope.core.persistence.jpa.entity.JPAExternalResource;
 import org.apache.syncope.core.persistence.jpa.validation.entity.PropagationTaskCheck;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
@@ -38,12 +43,14 @@
  * Encapsulate all information about a propagation task.
  */
 @Entity
-@DiscriminatorValue("PropagationTask")
+@Table(name = JPAPropagationTask.TABLE)
 @PropagationTaskCheck
-public class JPAPropagationTask extends AbstractTask implements PropagationTask {
+public class JPAPropagationTask extends AbstractTask<PropagationTask> implements PropagationTask {
 
     private static final long serialVersionUID = 7086054884614511210L;
 
+    public static final String TABLE = "PropagationTask";
+
     /**
      * @see ResourceOperation
      */
@@ -81,6 +88,10 @@
     @ManyToOne
     private JPAExternalResource resource;
 
+    @OneToMany(targetEntity = JPAPropagationTaskExec.class,
+            cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "task")
+    private List<TaskExec<PropagationTask>> executions = new ArrayList<>();
+
     @Override
     public String getConnObjectKey() {
         return connObjectKey;
@@ -132,17 +143,6 @@
     }
 
     @Override
-    public ExternalResource getResource() {
-        return resource;
-    }
-
-    @Override
-    public void setResource(final ExternalResource resource) {
-        checkType(resource, JPAExternalResource.class);
-        this.resource = (JPAExternalResource) resource;
-    }
-
-    @Override
     public String getObjectClassName() {
         return objectClassName;
     }
@@ -181,4 +181,25 @@
     public void setEntityKey(final String entityKey) {
         this.entityKey = entityKey;
     }
+
+    @Override
+    public ExternalResource getResource() {
+        return resource;
+    }
+
+    @Override
+    public void setResource(final ExternalResource resource) {
+        checkType(resource, JPAExternalResource.class);
+        this.resource = (JPAExternalResource) resource;
+    }
+
+    @Override
+    protected Class<? extends TaskExec<PropagationTask>> executionClass() {
+        return JPAPropagationTaskExec.class;
+    }
+
+    @Override
+    protected List<TaskExec<PropagationTask>> executions() {
+        return executions;
+    }
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPropagationTaskExec.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPropagationTaskExec.java
new file mode 100644
index 0000000..6653f48
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPropagationTaskExec.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.persistence.jpa.entity.task;
+
+import javax.persistence.Entity;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
+import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
+
+@Entity
+@Table(name = JPAPropagationTaskExec.TABLE)
+public class JPAPropagationTaskExec extends AbstractTaskExec<PropagationTask> implements TaskExec<PropagationTask> {
+
+    private static final long serialVersionUID = 1909033231464074554L;
+
+    public static final String TABLE = "PropagationTaskExec";
+
+    @ManyToOne(optional = false)
+    private JPAPropagationTask task;
+
+    @Override
+    public PropagationTask getTask() {
+        return task;
+    }
+
+    @Override
+    public void setTask(final PropagationTask task) {
+        checkType(task, PropagationTask.class);
+        this.task = (JPAPropagationTask) task;
+    }
+}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTask.java
index 31cc9e2..cb63027 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTask.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTask.java
@@ -22,7 +22,6 @@
 import java.util.List;
 import java.util.Optional;
 import javax.persistence.CascadeType;
-import javax.persistence.DiscriminatorValue;
 import javax.persistence.Entity;
 import javax.persistence.EnumType;
 import javax.persistence.Enumerated;
@@ -33,6 +32,7 @@
 import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
 import javax.persistence.OneToOne;
+import javax.persistence.Table;
 import javax.persistence.UniqueConstraint;
 import javax.validation.constraints.NotNull;
 import org.apache.syncope.common.lib.types.IdMImplementationType;
@@ -41,15 +41,19 @@
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.task.AnyTemplatePullTask;
 import org.apache.syncope.core.persistence.api.entity.task.PullTask;
+import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
+import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation;
 import org.apache.syncope.core.persistence.jpa.entity.JPARealm;
 
 @Entity
-@DiscriminatorValue("PullTask")
-public class JPAPullTask extends AbstractProvisioningTask implements PullTask {
+@Table(name = JPAPullTask.TABLE)
+public class JPAPullTask extends AbstractProvisioningTask<PullTask> implements PullTask {
 
     private static final long serialVersionUID = -4141057723006682563L;
 
+    public static final String TABLE = "PullTask";
+
     @Enumerated(EnumType.STRING)
     @NotNull
     private PullMode pullMode;
@@ -73,6 +77,10 @@
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "pullTask")
     private List<JPAAnyTemplatePullTask> templates = new ArrayList<>();
 
+    @OneToMany(targetEntity = JPAPullTaskExec.class,
+            cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "task")
+    private List<TaskExec<SchedTask>> executions = new ArrayList<>();
+
     @NotNull
     private Boolean remediation = false;
 
@@ -148,4 +156,14 @@
     public boolean isRemediation() {
         return remediation;
     }
+
+    @Override
+    protected Class<? extends TaskExec<SchedTask>> executionClass() {
+        return JPAPullTaskExec.class;
+    }
+
+    @Override
+    protected List<TaskExec<SchedTask>> executions() {
+        return executions;
+    }
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTaskExec.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTaskExec.java
new file mode 100644
index 0000000..2385084
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTaskExec.java
@@ -0,0 +1,49 @@
+/*
+ * 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.persistence.jpa.entity.task;
+
+import javax.persistence.Entity;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import org.apache.syncope.core.persistence.api.entity.task.PullTask;
+import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
+import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
+
+@Entity
+@Table(name = JPAPullTaskExec.TABLE)
+public class JPAPullTaskExec extends AbstractTaskExec<SchedTask> implements TaskExec<SchedTask> {
+
+    private static final long serialVersionUID = 1909033231464074554L;
+
+    public static final String TABLE = "PullTaskExec";
+
+    @ManyToOne(optional = false)
+    private JPAPullTask task;
+
+    @Override
+    public PullTask getTask() {
+        return task;
+    }
+
+    @Override
+    public void setTask(final SchedTask task) {
+        checkType(task, PullTask.class);
+        this.task = (JPAPullTask) task;
+    }
+}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPushTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPushTask.java
index 5c314db..a52922e 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPushTask.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPushTask.java
@@ -22,7 +22,6 @@
 import java.util.List;
 import java.util.Optional;
 import javax.persistence.CascadeType;
-import javax.persistence.DiscriminatorValue;
 import javax.persistence.Entity;
 import javax.persistence.FetchType;
 import javax.persistence.JoinColumn;
@@ -30,6 +29,7 @@
 import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
+import javax.persistence.Table;
 import javax.persistence.UniqueConstraint;
 import org.apache.syncope.common.lib.types.IdMImplementationType;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
@@ -37,15 +37,19 @@
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.task.PushTask;
 import org.apache.syncope.core.persistence.api.entity.task.PushTaskAnyFilter;
+import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
+import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation;
 import org.apache.syncope.core.persistence.jpa.entity.JPARealm;
 
 @Entity
-@DiscriminatorValue("PushTask")
-public class JPAPushTask extends AbstractProvisioningTask implements PushTask {
+@Table(name = JPAPushTask.TABLE)
+public class JPAPushTask extends AbstractProvisioningTask<PushTask> implements PushTask {
 
     private static final long serialVersionUID = -4141057723006682564L;
 
+    public static final String TABLE = "PushTask";
+
     @ManyToOne(fetch = FetchType.EAGER, optional = false)
     private JPARealm sourceRealm;
 
@@ -62,6 +66,10 @@
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "pushTask")
     private List<JPAPushTaskAnyFilter> filters = new ArrayList<>();
 
+    @OneToMany(targetEntity = JPAPushTaskExec.class,
+            cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "task")
+    private List<TaskExec<SchedTask>> executions = new ArrayList<>();
+
     @Override
     public JPARealm getSourceRealm() {
         return sourceRealm;
@@ -100,4 +108,14 @@
     public List<? extends PushTaskAnyFilter> getFilters() {
         return filters;
     }
+
+    @Override
+    protected Class<? extends TaskExec<SchedTask>> executionClass() {
+        return JPAPushTaskExec.class;
+    }
+
+    @Override
+    protected List<TaskExec<SchedTask>> executions() {
+        return executions;
+    }
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPushTaskExec.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPushTaskExec.java
new file mode 100644
index 0000000..eef50c0
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPushTaskExec.java
@@ -0,0 +1,49 @@
+/*
+ * 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.persistence.jpa.entity.task;
+
+import javax.persistence.Entity;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import org.apache.syncope.core.persistence.api.entity.task.PushTask;
+import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
+import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
+
+@Entity
+@Table(name = JPAPushTaskExec.TABLE)
+public class JPAPushTaskExec extends AbstractTaskExec<SchedTask> implements TaskExec<SchedTask> {
+
+    private static final long serialVersionUID = 1909033231464074554L;
+
+    public static final String TABLE = "PushTaskExec";
+
+    @ManyToOne(optional = false)
+    private JPAPushTask task;
+
+    @Override
+    public PushTask getTask() {
+        return task;
+    }
+
+    @Override
+    public void setTask(final SchedTask task) {
+        checkType(task, PushTask.class);
+        this.task = (JPAPushTask) task;
+    }
+}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTask.java
index 89dbdb0..aca10f3 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTask.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTask.java
@@ -19,30 +19,40 @@
 package org.apache.syncope.core.persistence.jpa.entity.task;
 
 import java.time.OffsetDateTime;
-import javax.persistence.DiscriminatorValue;
+import java.util.ArrayList;
+import java.util.List;
+import javax.persistence.CascadeType;
 import javax.persistence.Entity;
+import javax.persistence.Inheritance;
+import javax.persistence.InheritanceType;
+import javax.persistence.OneToMany;
 import javax.persistence.OneToOne;
+import javax.persistence.Table;
 import javax.validation.constraints.NotNull;
 import org.apache.syncope.common.lib.types.IdRepoImplementationType;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
+import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation;
 import org.apache.syncope.core.persistence.jpa.validation.entity.SchedTaskCheck;
 
 @Entity
-@DiscriminatorValue("SchedTask")
+@Table(name = JPASchedTask.TABLE)
+@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
 @SchedTaskCheck
-public class JPASchedTask extends AbstractTask implements SchedTask {
+public class JPASchedTask extends AbstractTask<SchedTask> implements SchedTask {
 
     private static final long serialVersionUID = 7596236684832602180L;
 
+    public static final String TABLE = "SchedTask";
+
+    @OneToOne(optional = false)
+    private JPAImplementation jobDelegate;
+
     private OffsetDateTime startAt;
 
     private String cronExpression;
 
-    @OneToOne(optional = false)
-    private JPAImplementation jobDelegate;
-
     @NotNull
     private String name;
 
@@ -51,6 +61,32 @@
     @NotNull
     private Boolean active = true;
 
+    @OneToMany(targetEntity = JPASchedTaskExec.class,
+            cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "task")
+    private List<TaskExec<SchedTask>> executions = new ArrayList<>();
+
+    @Override
+    protected Class<? extends TaskExec<SchedTask>> executionClass() {
+        return JPASchedTaskExec.class;
+    }
+
+    @Override
+    protected List<TaskExec<SchedTask>> executions() {
+        return executions;
+    }
+
+    @Override
+    public Implementation getJobDelegate() {
+        return jobDelegate;
+    }
+
+    @Override
+    public void setJobDelegate(final Implementation jobDelegate) {
+        checkType(jobDelegate, JPAImplementation.class);
+        checkImplementationType(jobDelegate, IdRepoImplementationType.TASKJOB_DELEGATE);
+        this.jobDelegate = (JPAImplementation) jobDelegate;
+    }
+
     @Override
     public OffsetDateTime getStartAt() {
         return startAt;
@@ -72,18 +108,6 @@
     }
 
     @Override
-    public Implementation getJobDelegate() {
-        return jobDelegate;
-    }
-
-    @Override
-    public void setJobDelegate(final Implementation jobDelegate) {
-        checkType(jobDelegate, JPAImplementation.class);
-        checkImplementationType(jobDelegate, IdRepoImplementationType.TASKJOB_DELEGATE);
-        this.jobDelegate = (JPAImplementation) jobDelegate;
-    }
-
-    @Override
     public String getDescription() {
         return description;
     }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTaskExec.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTaskExec.java
new file mode 100644
index 0000000..8d35e22
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTaskExec.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.persistence.jpa.entity.task;
+
+import javax.persistence.Entity;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
+import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
+
+@Entity
+@Table(name = JPASchedTaskExec.TABLE)
+public class JPASchedTaskExec extends AbstractTaskExec<SchedTask> implements TaskExec<SchedTask> {
+
+    private static final long serialVersionUID = 1909033231464074554L;
+
+    public static final String TABLE = "SchedTaskExec";
+
+    @ManyToOne(optional = false)
+    private JPASchedTask task;
+
+    @Override
+    public SchedTask getTask() {
+        return task;
+    }
+
+    @Override
+    public void setTask(final SchedTask task) {
+        checkType(task, SchedTask.class);
+        this.task = (JPASchedTask) task;
+    }
+}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATaskUtils.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATaskUtils.java
index 74cd927..df6f3a8 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATaskUtils.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATaskUtils.java
@@ -52,7 +52,7 @@
     }
 
     @Override
-    public <T extends Task> Class<T> taskClass() {
+    public <T extends Task<T>> Class<T> taskClass() {
         Class<T> result = null;
 
         switch (type) {
@@ -83,7 +83,7 @@
     }
 
     @Override
-    public <T extends Task> T newTask() {
+    public <T extends Task<T>> T newTask() {
         T result = null;
 
         switch (type) {
@@ -146,7 +146,7 @@
 
     @Override
     public <T extends TaskTO> T newTaskTO() {
-        final Class<T> taskClass = taskTOClass();
+        Class<T> taskClass = taskTOClass();
         try {
             return taskClass == null ? null : taskClass.getDeclaredConstructor().newInstance();
         } catch (Exception e) {
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATaskUtilsFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATaskUtilsFactory.java
index 1818093..53c9575 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATaskUtilsFactory.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATaskUtilsFactory.java
@@ -64,7 +64,7 @@
     }
 
     @Override
-    public TaskUtils getInstance(final Task task) {
+    public TaskUtils getInstance(final Task<?> task) {
         TaskType type;
         if (task instanceof PullTask) {
             type = TaskType.PULL;
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java
index edabbfa..28fad86 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity.user;
 
+import com.fasterxml.jackson.core.type.TypeReference;
 import java.time.OffsetDateTime;
 import java.util.ArrayList;
 import java.util.List;
@@ -25,9 +26,7 @@
 import java.util.stream.Collectors;
 import javax.persistence.Cacheable;
 import javax.persistence.CascadeType;
-import javax.persistence.CollectionTable;
 import javax.persistence.Column;
-import javax.persistence.ElementCollection;
 import javax.persistence.Entity;
 import javax.persistence.EnumType;
 import javax.persistence.Enumerated;
@@ -62,6 +61,7 @@
 import org.apache.syncope.core.persistence.jpa.entity.JPAAnyTypeClass;
 import org.apache.syncope.core.persistence.jpa.entity.JPAExternalResource;
 import org.apache.syncope.core.persistence.jpa.entity.JPARole;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.apache.syncope.core.spring.security.Encryptor;
@@ -111,11 +111,8 @@
     @Enumerated(EnumType.STRING)
     private CipherAlgorithm cipherAlgorithm;
 
-    @ElementCollection
-    @Column(name = "passwordHistoryValue")
-    @CollectionTable(name = "SyncopeUser_passwordHistory", joinColumns =
-            @JoinColumn(name = "user_id", referencedColumnName = "id"))
-    private List<String> passwordHistory = new ArrayList<>();
+    @Lob
+    private String passwordHistory;
 
     /**
      * Subsequent failed logins.
@@ -340,8 +337,25 @@
     }
 
     @Override
+    public void addToPasswordHistory(final String password) {
+        List<String> ph = getPasswordHistory();
+        ph.add(password);
+        passwordHistory = POJOHelper.serialize(ph);
+    }
+
+    @Override
+    public void removeOldestEntriesFromPasswordHistory(final int n) {
+        List<String> ph = getPasswordHistory();
+        ph.subList(n, ph.size());
+        passwordHistory = POJOHelper.serialize(ph);
+    }
+
+    @Override
     public List<String> getPasswordHistory() {
-        return passwordHistory;
+        return passwordHistory == null
+                ? new ArrayList<>(0)
+                : POJOHelper.deserialize(passwordHistory, new TypeReference<List<String>>() {
+                });
     }
 
     @Override
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyValidator.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyValidator.java
index baa33f9..dbafb41 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyValidator.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyValidator.java
@@ -34,9 +34,9 @@
 public class AnyValidator extends AbstractValidator<AnyCheck, Any> {
 
     private static boolean raiseNotAllowedViolation(
-        final ConstraintValidatorContext context,
-        final String schema,
-        final Group group) {
+            final ConstraintValidatorContext context,
+            final String schema,
+            final Group group) {
 
         if (group == null) {
             context.buildConstraintViolationWithTemplate(
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 f8f24a6..ffa2ad1 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
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.core.persistence.jpa.validation.entity;
 
-import java.util.List;
 import javax.validation.ConstraintValidatorContext;
 import org.apache.syncope.common.lib.types.EntityViolationType;
 import org.apache.syncope.common.lib.types.ExecStatus;
@@ -29,33 +28,26 @@
 
     @Override
     public boolean isValid(final PropagationTask task, final ConstraintValidatorContext context) {
-        boolean isValid;
+        boolean isValid = task.getOperation() != null
+                && task.getPropagationData() != null
+                && task.getResource() != null;
 
-        if (task == null) {
-            isValid = true;
-        } else {
-            isValid = task.getOperation() != null
-                    && task.getPropagationData() != null
-                    && task.getResource() != null;
-
-            if (isValid) {
-                List<? extends TaskExec> executions = task.getExecs();
-                for (TaskExec execution : executions) {
-                    try {
-                        ExecStatus.valueOf(execution.getStatus());
-                    } catch (IllegalArgumentException e) {
-                        LOG.error("Invalid execution status '" + execution.getStatus() + '\'', e);
-                        isValid = false;
-                    }
+        if (isValid) {
+            for (TaskExec<PropagationTask> execution : task.getExecs()) {
+                try {
+                    ExecStatus.valueOf(execution.getStatus());
+                } catch (IllegalArgumentException e) {
+                    LOG.error("Invalid execution status '" + execution.getStatus() + '\'', e);
+                    isValid = false;
                 }
             }
+        }
 
-            if (!isValid) {
-                context.disableDefaultConstraintViolation();
-                context.buildConstraintViolationWithTemplate(
-                        getTemplate(EntityViolationType.InvalidPropagationTask, "Invalid task")).
-                        addPropertyNode(task.getClass().getSimpleName()).addConstraintViolation();
-            }
+        if (!isValid) {
+            context.disableDefaultConstraintViolation();
+            context.buildConstraintViolationWithTemplate(
+                    getTemplate(EntityViolationType.InvalidPropagationTask, "Invalid task")).
+                    addPropertyNode(task.getClass().getSimpleName()).addConstraintViolation();
         }
 
         return isValid;
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ProvisioningTaskValidator.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ProvisioningTaskValidator.java
index cdfb78b..c029746 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ProvisioningTaskValidator.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ProvisioningTaskValidator.java
@@ -22,19 +22,13 @@
 import org.apache.syncope.common.lib.types.EntityViolationType;
 import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask;
 
-public class ProvisioningTaskValidator extends AbstractValidator<ProvisioningTaskCheck, ProvisioningTask> {
+public class ProvisioningTaskValidator extends AbstractValidator<ProvisioningTaskCheck, ProvisioningTask<?>> {
 
-    private final SchedTaskValidator schedV;
-
-    public ProvisioningTaskValidator() {
-        super();
-
-        schedV = new SchedTaskValidator();
-    }
+    private final SchedTaskValidator schedTaskValidator = new SchedTaskValidator();
 
     @Override
-    public boolean isValid(final ProvisioningTask task, final ConstraintValidatorContext context) {
-        boolean isValid = schedV.isValid(task, context);
+    public boolean isValid(final ProvisioningTask<?> task, final ConstraintValidatorContext context) {
+        boolean isValid = schedTaskValidator.isValid(task, context);
 
         if (isValid) {
             isValid = task.getResource() != null;
diff --git a/core/persistence-jpa/src/main/resources/domains/MasterContent.xml b/core/persistence-jpa/src/main/resources/domains/MasterContent.xml
index 702837b..0d7ff8b 100644
--- a/core/persistence-jpa/src/main/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/main/resources/domains/MasterContent.xml
@@ -47,12 +47,12 @@
 
   <Implementation id="ExpiredAccessTokenCleanup" type="TASKJOB_DELEGATE" engine="JAVA"
                   body="org.apache.syncope.core.provisioning.java.job.ExpiredAccessTokenCleanup"/>
-  <Task DTYPE="SchedTask" id="89de5014-e3f5-4462-84d8-d97575740baf" name="Access Token Cleanup Task"  active="1"
-        jobDelegate_id="ExpiredAccessTokenCleanup" cronExpression="0 0/5 * * * ?"/>
+  <SchedTask id="89de5014-e3f5-4462-84d8-d97575740baf" name="Access Token Cleanup Task"  active="1"
+             jobDelegate_id="ExpiredAccessTokenCleanup" cronExpression="0 0/5 * * * ?"/>
   <Implementation id="ExpiredBatchCleanup" type="TASKJOB_DELEGATE" engine="JAVA"
                   body="org.apache.syncope.core.provisioning.java.job.ExpiredBatchCleanup"/>
-  <Task DTYPE="SchedTask" id="8ea0ea51-ce08-4fe3-a0c8-c281b31b5893" name="Expired Batch Operations Cleanup Task"  active="1"
-        jobDelegate_id="ExpiredBatchCleanup" cronExpression="0 0/5 * * * ?"/>
+  <SchedTask id="8ea0ea51-ce08-4fe3-a0c8-c281b31b5893" name="Expired Batch Operations Cleanup Task"  active="1"
+             jobDelegate_id="ExpiredBatchCleanup" cronExpression="0 0/5 * * * ?"/>
 
   <!-- Password reset notifications -->
   <MailTemplate id="requestPasswordReset"
@@ -95,14 +95,12 @@
 
   <Notification id="e00945b5-1184-4d43-8e45-4318a8dcdfd4" active="1" recipientAttrName="email" selfAsRecipient="1" 
                 sender="admin@syncope.apache.org" subject="Password Reset request" template_id="requestPasswordReset" 
-                traceLevel="FAILURES"/> 
+                traceLevel="FAILURES" events='["[CUSTOM]:[]:[]:[requestPasswordReset]:[SUCCESS]"]'/> 
   <AnyAbout id="a328f2e6-25e9-4cc1-badf-7425d7be4b39" anyType_id="USER" notification_id="e00945b5-1184-4d43-8e45-4318a8dcdfd4" anyType_filter="token!=$null"/>
-  <Notification_events notification_id="e00945b5-1184-4d43-8e45-4318a8dcdfd4" event="[CUSTOM]:[]:[]:[requestPasswordReset]:[SUCCESS]"/>
   
   <Notification id="bef0c250-e8a7-4848-bb63-2564fc409ce2" active="1" recipientAttrName="email" selfAsRecipient="1" 
                 sender="admin@syncope.apache.org" subject="Password Reset successful" template_id="confirmPasswordReset" 
-                traceLevel="FAILURES"/> 
-  <Notification_events notification_id="bef0c250-e8a7-4848-bb63-2564fc409ce2" event="[CUSTOM]:[]:[]:[confirmPasswordReset]:[SUCCESS]"/>
+                traceLevel="FAILURES" events='["[CUSTOM]:[]:[]:[confirmPasswordReset]:[SUCCESS]"]'/> 
 
   <ReportTemplate id="empty"/>  
 
@@ -111,20 +109,5 @@
                   body='{"_class":"org.apache.syncope.common.lib.report.ReconciliationReportletConf","name":"dashboardReconciliationReportlet","userMatchingCond":null,"groupMatchingCond":null,"anyObjectMatchingCond":null,"features":["key","username","groupName"]}'/>
   <ReportReportlet report_id="c3520ad9-179f-49e7-b315-d684d216dd97" implementation_id="ReconciliationReportletConf"/>
 
-  <SyncopeRole id="GROUP_OWNER"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_SEARCH"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_CREATE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_UPDATE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_DELETE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPECLASS_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPE_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPECLASS_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="RELATIONSHIPTYPE_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPE_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="REALM_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_SEARCH"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_UPDATE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_DELETE"/>
+  <SyncopeRole id="GROUP_OWNER" entitlements='["USER_SEARCH","USER_READ","USER_CREATE","USER_UPDATE","USER_DELETE","ANYTYPECLASS_READ","ANYTYPE_LIST","ANYTYPECLASS_LIST","RELATIONSHIPTYPE_LIST","ANYTYPE_READ","REALM_LIST","GROUP_SEARCH","GROUP_READ","GROUP_UPDATE","GROUP_DELETE"]'/>
 </dataset>
diff --git a/core/persistence-jpa/src/main/resources/indexes.xml b/core/persistence-jpa/src/main/resources/indexes.xml
index 62f3a8d..939eb70 100644
--- a/core/persistence-jpa/src/main/resources/indexes.xml
+++ b/core/persistence-jpa/src/main/resources/indexes.xml
@@ -80,8 +80,11 @@
   <entry key="APlainAttr_schema_Index">CREATE INDEX APlainAttr_schema_Index on APlainAttr(schema_id)</entry>
   <entry key="APlainAttr_membership_Index">CREATE INDEX APlainAttr_membership_Index on APlainAttr(membership_id)</entry>
 
-  <entry key="Task_executedIndex">CREATE INDEX Task_executedIndex ON Task(executed)</entry>
-  <entry key="TaskExec_TaskIdIndex">CREATE INDEX TaskExec_TaskIdIndex ON TaskExec(task_id)</entry>
+  <entry key="Task_executedIndex">CREATE INDEX Task_executedIndex ON NotificationTask(executed)</entry>
+  <entry key="TaskExec1_TaskIdIndex">CREATE INDEX TaskExec1_TaskIdIndex ON PropagationTaskExec(task_id)</entry>
+  <entry key="TaskExec2_TaskIdIndex">CREATE INDEX TaskExec2_TaskIdIndex ON PullTaskExec(task_id)</entry>
+  <entry key="TaskExec3_TaskIdIndex">CREATE INDEX TaskExec3_TaskIdIndex ON PushTaskExec(task_id)</entry>
+  <entry key="TaskExec4_TaskIdIndex">CREATE INDEX TaskExec4_TaskIdIndex ON NotificationTaskExec(task_id)</entry>
+  <entry key="TaskExec5_TaskIdIndex">CREATE INDEX TaskExec5_TaskIdIndex ON SchedTaskExec(task_id)</entry>
   <entry key="ATPullTask_PullTaskIndex">CREATE INDEX ATPullTask_PullTaskIndex ON AnyTemplatePullTask(pullTask_id)</entry>
-  <entry key="NT_recipientsIndex">CREATE INDEX NT_recipientsIndex ON NotificationTask_recipients(notificationTask_id)</entry>
 </properties>
diff --git a/core/persistence-jpa/src/main/resources/oracle_indexes.xml b/core/persistence-jpa/src/main/resources/oracle_indexes.xml
index 6c96039..a996d33 100644
--- a/core/persistence-jpa/src/main/resources/oracle_indexes.xml
+++ b/core/persistence-jpa/src/main/resources/oracle_indexes.xml
@@ -84,8 +84,11 @@
   <entry key="APlainAttr_schema_Index">CREATE INDEX APlainAttr_schema_Index on APlainAttr(schema_id)</entry>
   <entry key="APlainAttr_membership_Index">CREATE INDEX APlainAttr_membership_Index on APlainAttr(membership_id)</entry>
 
-  <entry key="Task_executedIndex">CREATE INDEX Task_executedIndex ON Task(executed)</entry>
-  <entry key="TaskExec_TaskIdIndex">CREATE INDEX TaskExec_TaskIdIndex ON TaskExec(task_id)</entry>
+  <entry key="Task_executedIndex">CREATE INDEX Task_executedIndex ON NotificationTask(executed)</entry>
+  <entry key="TaskExec1_TaskIdIndex">CREATE INDEX TaskExec1_TaskIdIndex ON PropagationTaskExec(task_id)</entry>
+  <entry key="TaskExec2_TaskIdIndex">CREATE INDEX TaskExec2_TaskIdIndex ON PullTaskExec(task_id)</entry>
+  <entry key="TaskExec3_TaskIdIndex">CREATE INDEX TaskExec3_TaskIdIndex ON PushTaskExec(task_id)</entry>
+  <entry key="TaskExec4_TaskIdIndex">CREATE INDEX TaskExec4_TaskIdIndex ON NotificationTaskExec(task_id)</entry>
+  <entry key="TaskExec5_TaskIdIndex">CREATE INDEX TaskExec5_TaskIdIndex ON SchedTaskExec(task_id)</entry>
   <entry key="ATPullTask_PullTaskIndex">CREATE INDEX ATPullTask_PullTaskIndex ON AnyTemplatePullTask(pullTask_id)</entry>
-  <entry key="NT_recipientsIndex">CREATE INDEX NT_recipientsIndex ON NotificationTask_recipients(notificationTask_id)</entry>
 </properties>
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/NotificationTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/NotificationTest.java
index 953cb9d..9545c13 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/NotificationTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/NotificationTest.java
@@ -114,7 +114,11 @@
         notification.setSubject("Test notification");
         notification.setTemplate(mailTemplateDAO.find("test"));
 
-        Notification actual = notificationDAO.save(notification);
+        notification = notificationDAO.save(notification);
+
+        entityManager().flush();
+
+        Notification actual = notificationDAO.find(notification.getKey());
         assertNotNull(actual);
         assertNotNull(actual.getKey());
         assertNotNull(actual.getStaticRecipients());
@@ -140,7 +144,11 @@
         notification.setSubject("Test notification");
         notification.setTemplate(mailTemplateDAO.find("test"));
 
-        Notification actual = notificationDAO.save(notification);
+        notification = notificationDAO.save(notification);
+
+        entityManager().flush();
+
+        Notification actual = notificationDAO.find(notification.getKey());
         assertNotNull(actual);
         assertNotNull(actual.getKey());
         assertNotNull(actual.getStaticRecipients());
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RemediationTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RemediationTest.java
index 19e290f..fef2199 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RemediationTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RemediationTest.java
@@ -30,11 +30,13 @@
 import java.util.UUID;
 import org.apache.syncope.common.lib.types.EntityViolationType;
 import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.common.lib.types.TaskType;
 import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
 import org.apache.syncope.core.persistence.api.dao.RemediationDAO;
 import org.apache.syncope.core.persistence.api.dao.TaskDAO;
 import org.apache.syncope.core.persistence.api.entity.Remediation;
+import org.apache.syncope.core.persistence.api.entity.task.PullTask;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -66,7 +68,7 @@
         remediation.setError("Error");
         remediation.setInstant(OffsetDateTime.now());
         remediation.setRemoteName("remote");
-        remediation.setPullTask(taskDAO.find("38abbf9e-a1a3-40a1-a15f-7d0ac02f47f1"));
+        remediation.setPullTask((PullTask) taskDAO.find(TaskType.PULL, "38abbf9e-a1a3-40a1-a15f-7d0ac02f47f1"));
 
         // missing payload
         try {
@@ -87,7 +89,7 @@
         remediation.setError("Error");
         remediation.setInstant(OffsetDateTime.now());
         remediation.setRemoteName("remote");
-        remediation.setPullTask(taskDAO.find("38abbf9e-a1a3-40a1-a15f-7d0ac02f47f1"));
+        remediation.setPullTask((PullTask) taskDAO.find(TaskType.PULL, "38abbf9e-a1a3-40a1-a15f-7d0ac02f47f1"));
         remediation.setPayload(UUID.randomUUID().toString());
 
         // wrong payload for operation
@@ -109,7 +111,7 @@
         remediation.setError("Error");
         remediation.setInstant(OffsetDateTime.now());
         remediation.setRemoteName("remote");
-        remediation.setPullTask(taskDAO.find("38abbf9e-a1a3-40a1-a15f-7d0ac02f47f1"));
+        remediation.setPullTask((PullTask) taskDAO.find(TaskType.PULL, "38abbf9e-a1a3-40a1-a15f-7d0ac02f47f1"));
         remediation.setPayload(UUID.randomUUID().toString());
         remediation.setOperation(ResourceOperation.DELETE);
 
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 5bdbc3b..38d76b2 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,6 +24,7 @@
 import java.time.OffsetDateTime;
 import java.util.List;
 import org.apache.syncope.common.lib.types.ExecStatus;
+import org.apache.syncope.common.lib.types.TaskType;
 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;
@@ -45,35 +46,35 @@
 
     @Test
     public void findAll() {
-        PropagationTask task = taskDAO.find("1e697572-b896-484c-ae7f-0c8f63fcbc6c");
+        PropagationTask task = taskDAO.find(TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c");
         assertNotNull(task);
 
         OffsetDateTime startedBefore = OffsetDateTime.of(2015, 12, 18, 0, 0, 0, 0, FormatUtils.DEFAULT_OFFSET);
 
-        List<TaskExec> execs = taskExecDAO.findAll(task, startedBefore, null, null, null);
+        List<TaskExec<?>> execs = taskExecDAO.findAll(task, startedBefore, null, null, null);
         assertNotNull(execs);
         assertEquals(1, execs.size());
     }
 
     @Test
     public void findLatestStarted() {
-        PropagationTask task = taskDAO.find("1e697572-b896-484c-ae7f-0c8f63fcbc6c");
+        PropagationTask task = taskDAO.find(TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c");
         assertNotNull(task);
 
-        TaskExec latestStarted = taskExecDAO.findLatestStarted(task);
+        TaskExec<?> latestStarted = taskExecDAO.findLatestStarted(TaskType.PROPAGATION, task);
         assertNotNull(latestStarted);
         assertEquals("e58ca1c7-178a-4012-8a71-8aa14eaf0655", latestStarted.getKey());
     }
 
     @Test
     public void issueSYNCOPE214() {
-        PropagationTask task = taskDAO.find("1e697572-b896-484c-ae7f-0c8f63fcbc6c");
+        PropagationTask task = taskDAO.find(TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c");
         assertNotNull(task);
 
         String faultyMessage = "A faulty message";
         faultyMessage = faultyMessage.replace('a', '\0');
 
-        TaskExec exec = entityFactory.newEntity(TaskExec.class);
+        TaskExec<PropagationTask> exec = entityFactory.newTaskExec(TaskType.PROPAGATION);
         exec.setStart(OffsetDateTime.now());
         exec.setEnd(OffsetDateTime.now());
         exec.setStatus(ExecStatus.SUCCESS.name());
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskTest.java
index 87cfd6d..7578746 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskTest.java
@@ -36,7 +36,6 @@
 import org.apache.syncope.core.persistence.api.entity.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.task.PropagationData;
 import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
-import org.apache.syncope.core.persistence.api.entity.task.Task;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
 import org.identityconnectors.framework.common.objects.Attribute;
@@ -66,26 +65,23 @@
 
     @Test
     public void findPaginated() {
-        List<Task> tasks = taskDAO.findAll(
-                TaskType.PROPAGATION, null, null, null, null, 1, 2, List.of());
+        List<PropagationTask> tasks = taskDAO.findAll(TaskType.PROPAGATION, null, null, null, null, 1, 2, List.of());
         assertNotNull(tasks);
         assertEquals(2, tasks.size());
 
-        for (Task task : tasks) {
+        for (PropagationTask task : tasks) {
             assertNotNull(task);
         }
 
-        tasks = taskDAO.findAll(
-                TaskType.PROPAGATION, null, null, null, null, 2, 2, List.of());
+        tasks = taskDAO.findAll(TaskType.PROPAGATION, null, null, null, null, 2, 2, List.of());
         assertNotNull(tasks);
         assertEquals(2, tasks.size());
 
-        for (Task task : tasks) {
+        for (PropagationTask task : tasks) {
             assertNotNull(task);
         }
 
-        tasks = taskDAO.findAll(
-                TaskType.PROPAGATION, null, null, null, null, 1000, 2, List.of());
+        tasks = taskDAO.findAll(TaskType.PROPAGATION, null, null, null, null, 1000, 2, List.of());
         assertNotNull(tasks);
         assertTrue(tasks.isEmpty());
 
@@ -124,25 +120,25 @@
         task = taskDAO.save(task);
         assertNotNull(task);
 
-        PropagationTask actual = taskDAO.find(task.getKey());
+        PropagationTask actual = taskDAO.find(TaskType.PROPAGATION, task.getKey());
         assertEquals(task, actual);
     }
 
     @Test
     public void delete() {
-        PropagationTask task = taskDAO.find("1e697572-b896-484c-ae7f-0c8f63fcbc6c");
+        PropagationTask task = taskDAO.find(TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c");
         assertNotNull(task);
 
         ExternalResource resource = task.getResource();
         assertNotNull(resource);
 
         taskDAO.delete(task);
-        task = taskDAO.find("1e697572-b896-484c-ae7f-0c8f63fcbc6c");
+        task = taskDAO.find(TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c");
         assertNull(task);
 
         resource = resourceDAO.find(resource.getKey());
         assertNotNull(resource);
-        assertFalse(taskDAO.findAll(
+        assertFalse(taskDAO.<PropagationTask>findAll(
                 TaskType.PROPAGATION, resource, null, null, null, -1, -1, List.of()).
                 contains(task));
     }
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceTest.java
index 728a70f..dc7a929 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceTest.java
@@ -232,7 +232,7 @@
                 forEach(res -> assertFalse(res.getKey().equalsIgnoreCase(resource.getKey())));
 
         // there must be no tasks
-        propagationTasks.forEach(task -> assertNull(taskDAO.find(task.getKey())));
+        propagationTasks.forEach(task -> assertTrue(taskDAO.find(task.getKey()).isEmpty()));
     }
 
     @Test
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 2a6749c..47a5b41 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
@@ -21,7 +21,6 @@
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.time.OffsetDateTime;
@@ -54,6 +53,7 @@
 import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 import org.apache.syncope.core.persistence.api.entity.task.PullTask;
 import org.apache.syncope.core.persistence.api.entity.task.PushTask;
+import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
 import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
@@ -84,7 +84,7 @@
 
     @Test
     public void read() {
-        PropagationTask task = taskDAO.find("1e697572-b896-484c-ae7f-0c8f63fcbc6c");
+        PropagationTask task = taskDAO.find(TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c");
         assertNotNull(task);
 
         assertNotNull(task.getExecs());
@@ -130,25 +130,25 @@
         task = taskDAO.save(task);
         assertNotNull(task);
 
-        PropagationTask actual = taskDAO.find(task.getKey());
+        PropagationTask actual = taskDAO.find(TaskType.PROPAGATION, task.getKey());
         assertEquals(task, actual);
 
         entityManager().flush();
 
         resource = resourceDAO.find("ws-target-resource-1");
-        assertTrue(taskDAO.findAll(
+        assertTrue(taskDAO.<PropagationTask>findAll(
                 TaskType.PROPAGATION, resource, null, null, null, -1, -1, List.of()).
                 contains(task));
     }
 
     @Test
     public void addPropagationTaskExecution() {
-        PropagationTask task = taskDAO.find("1e697572-b896-484c-ae7f-0c8f63fcbc6c");
+        PropagationTask task = taskDAO.find(TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c");
         assertNotNull(task);
 
         int executionNumber = task.getExecs().size();
 
-        TaskExec execution = entityFactory.newEntity(TaskExec.class);
+        TaskExec<PropagationTask> execution = entityFactory.newTaskExec(TaskType.PROPAGATION);
         execution.setTask(task);
         execution.setStatus(ExecStatus.CREATED.name());
         execution.setStart(OffsetDateTime.now());
@@ -158,7 +158,7 @@
         taskDAO.save(task);
         entityManager().flush();
 
-        task = taskDAO.find("1e697572-b896-484c-ae7f-0c8f63fcbc6c");
+        task = taskDAO.find(TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c");
         assertNotNull(task);
 
         assertEquals(executionNumber + 1, task.getExecs().size());
@@ -166,12 +166,12 @@
 
     @Test
     public void addPullTaskExecution() {
-        PullTask task = taskDAO.find("c41b9b71-9bfa-4f90-89f2-84787def4c5c");
+        PullTask task = (PullTask) taskDAO.find(TaskType.PULL, "c41b9b71-9bfa-4f90-89f2-84787def4c5c");
         assertNotNull(task);
 
         int executionNumber = task.getExecs().size();
 
-        TaskExec execution = entityFactory.newEntity(TaskExec.class);
+        TaskExec<SchedTask> execution = entityFactory.newTaskExec(TaskType.PULL);
         execution.setStatus("Text-free status");
         execution.setTask(task);
         execution.setStart(OffsetDateTime.now());
@@ -182,7 +182,7 @@
         taskDAO.save(task);
         entityManager().flush();
 
-        task = taskDAO.find("c41b9b71-9bfa-4f90-89f2-84787def4c5c");
+        task = (PullTask) taskDAO.find(TaskType.PULL, "c41b9b71-9bfa-4f90-89f2-84787def4c5c");
         assertNotNull(task);
 
         assertEquals(executionNumber + 1, task.getExecs().size());
@@ -190,12 +190,12 @@
 
     @Test
     public void addPushTaskExecution() {
-        PushTask task = taskDAO.find("af558be4-9d2f-4359-bf85-a554e6e90be1");
+        PushTask task = (PushTask) taskDAO.find(TaskType.PUSH, "af558be4-9d2f-4359-bf85-a554e6e90be1");
         assertNotNull(task);
 
         int executionNumber = task.getExecs().size();
 
-        TaskExec execution = entityFactory.newEntity(TaskExec.class);
+        TaskExec<SchedTask> execution = entityFactory.newTaskExec(TaskType.PUSH);
         execution.setStatus("Text-free status");
         execution.setTask(task);
         execution.setStart(OffsetDateTime.now());
@@ -206,7 +206,7 @@
         taskDAO.save(task);
         entityManager().flush();
 
-        task = taskDAO.find("af558be4-9d2f-4359-bf85-a554e6e90be1");
+        task = (PushTask) taskDAO.find(TaskType.PUSH, "af558be4-9d2f-4359-bf85-a554e6e90be1");
         assertNotNull(task);
 
         assertEquals(executionNumber + 1, task.getExecs().size());
@@ -214,26 +214,27 @@
 
     @Test
     public void deleteTask() {
-        taskDAO.delete("1e697572-b896-484c-ae7f-0c8f63fcbc6c");
+        taskDAO.delete(TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c");
 
         entityManager().flush();
 
-        assertNull(taskDAO.find("1e697572-b896-484c-ae7f-0c8f63fcbc6c"));
-        assertNull(taskExecDAO.find("e58ca1c7-178a-4012-8a71-8aa14eaf0655"));
+        assertTrue(taskDAO.find("1e697572-b896-484c-ae7f-0c8f63fcbc6c").isEmpty());
+        assertTrue(taskExecDAO.find("e58ca1c7-178a-4012-8a71-8aa14eaf0655").isEmpty());
     }
 
     @Test
     public void deleteTaskExecution() {
-        TaskExec execution = taskExecDAO.find("e58ca1c7-178a-4012-8a71-8aa14eaf0655");
+        TaskExec<PropagationTask> execution =
+                taskExecDAO.find(TaskType.PROPAGATION, "e58ca1c7-178a-4012-8a71-8aa14eaf0655");
         int executionNumber = execution.getTask().getExecs().size();
 
-        taskExecDAO.delete("e58ca1c7-178a-4012-8a71-8aa14eaf0655");
+        taskExecDAO.delete(TaskType.PROPAGATION, "e58ca1c7-178a-4012-8a71-8aa14eaf0655");
 
         entityManager().flush();
 
-        assertNull(taskExecDAO.find("e58ca1c7-178a-4012-8a71-8aa14eaf0655"));
+        assertTrue(taskExecDAO.find("e58ca1c7-178a-4012-8a71-8aa14eaf0655").isEmpty());
 
-        PropagationTask task = taskDAO.find("1e697572-b896-484c-ae7f-0c8f63fcbc6c");
+        PropagationTask task = taskDAO.find(TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c");
         assertEquals(task.getExecs().size(), executionNumber - 1);
     }
 
@@ -286,10 +287,10 @@
         task.add(pullActions);
 
         // this save() finally works
-        task = taskDAO.save(task);
+        task = (PullTask) taskDAO.save(task);
         assertNotNull(task);
 
-        PullTask actual = taskDAO.find(task.getKey());
+        PullTask actual = (PullTask) taskDAO.find(TaskType.PULL, task.getKey());
         assertEquals(task, actual);
     }
 
@@ -316,10 +317,10 @@
         task.setMatchingRule(MatchingRule.UPDATE);
         task.setUnmatchingRule(UnmatchingRule.PROVISION);
 
-        task = taskDAO.save(task);
+        task = (PullTask) taskDAO.save(task);
         assertNotNull(task);
 
-        PullTask actual = taskDAO.find(task.getKey());
+        PullTask actual = (PullTask) taskDAO.find(TaskType.PULL, task.getKey());
         assertEquals(task, actual);
         assertEquals("issueSYNCOPE144", actual.getName());
         assertEquals("issueSYNCOPE144 Description", actual.getDescription());
@@ -327,10 +328,9 @@
         actual.setName("issueSYNCOPE144_2");
         actual.setDescription("issueSYNCOPE144 Description_2");
 
-        actual = taskDAO.save(actual);
+        actual = (PullTask) taskDAO.save(actual);
         assertNotNull(actual);
         assertEquals("issueSYNCOPE144_2", actual.getName());
         assertEquals("issueSYNCOPE144 Description_2", actual.getDescription());
     }
-
 }
diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index 0acb0a1..4c49dd8 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -154,51 +154,21 @@
              spec="{ &quot;method&quot;: &quot;GET&quot;, &quot;url&quot;: &quot;/a/b/c&quot; }"
              application_id="mightyApp"/>
 
-  <SyncopeRole id="User reviewer"/>
-  <SyncopeRole_entitlements entitlement="USER_READ" role_id="User reviewer"/>
-  <SyncopeRole_entitlements entitlement="USER_LIST" role_id="User reviewer"/>
-  <SyncopeRole_entitlements entitlement="USER_SEARCH" role_id="User reviewer"/>
-  <SyncopeRole_entitlements entitlement="ANYTYPE_LIST" role_id="User reviewer"/>
-  <SyncopeRole_entitlements entitlement="ANYTYPE_READ" role_id="User reviewer"/>
-  <SyncopeRole_entitlements entitlement="ANYTYPECLASS_LIST" role_id="User reviewer"/>
-  <SyncopeRole_entitlements entitlement="ANYTYPECLASS_READ" role_id="User reviewer"/>
+  <SyncopeRole id="User reviewer" entitlements='["USER_READ","USER_LIST","USER_SEARCH","ANYTYPE_LIST","ANYTYPE_READ","ANYTYPECLASS_LIST","ANYTYPECLASS_READ"]'/>
   <SyncopeRole_Realm role_id="User reviewer" realm_id="722f3d84-9c2b-4525-8f6e-e4b82c55a36c"/>
   <SyncopeRole_Realm role_id="User reviewer" realm_id="c5b75db1-fce7-470f-b780-3b9934d82a9d"/>
   
-  <SyncopeRole id="User manager"/>
-  <SyncopeRole_entitlements entitlement="USER_READ" role_id="User manager"/>
-  <SyncopeRole_entitlements entitlement="USER_LIST" role_id="User manager"/>
-  <SyncopeRole_entitlements entitlement="USER_SEARCH" role_id="User manager"/>
-  <SyncopeRole_entitlements entitlement="ANYTYPE_LIST" role_id="User manager"/>
-  <SyncopeRole_entitlements entitlement="ANYTYPE_READ" role_id="User manager"/>
-  <SyncopeRole_entitlements entitlement="ANYTYPECLASS_LIST" role_id="User manager"/>
-  <SyncopeRole_entitlements entitlement="ANYTYPECLASS_READ" role_id="User manager"/>
-  <SyncopeRole_entitlements entitlement="USER_REQUEST_FORM_LIST" role_id="User manager"/>
-  <SyncopeRole_entitlements entitlement="USER_REQUEST_FORM_CLAIM" role_id="User manager"/>
-  <SyncopeRole_entitlements entitlement="USER_REQUEST_FORM_SUBMIT" role_id="User manager"/>
+  <SyncopeRole id="User manager" entitlements='["USER_READ","USER_LIST","USER_SEARCH","ANYTYPE_LIST","ANYTYPE_READ","ANYTYPECLASS_LIST","ANYTYPECLASS_READ","USER_REQUEST_FORM_LIST","USER_REQUEST_FORM_CLAIM","USER_REQUEST_FORM_SUBMIT"]'/>
   <SyncopeRole_Realm role_id="User manager" realm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"/>
 
-  <SyncopeRole id="Other"/>
-  <SyncopeRole_entitlements entitlement="SCHEMA_READ" role_id="Other"/>
-  <SyncopeRole_entitlements entitlement="GROUP_READ" role_id="Other"/>
-  <SyncopeRole_entitlements entitlement="USER_REQUEST_FORM_CLAIM" role_id="Other"/>
+  <SyncopeRole id="Other" entitlements='["SCHEMA_READ","GROUP_READ","USER_REQUEST_FORM_CLAIM"]'/>
   <SyncopeRole_Realm role_id="Other" realm_id="722f3d84-9c2b-4525-8f6e-e4b82c55a36c"/>
   <SyncopeRole_Privilege role_id="Other" privilege_id="postMighty"/>
   
-  <SyncopeRole id="Search for realm evenTwo"/>
-  <SyncopeRole_entitlements entitlement="USER_READ" role_id="Search for realm evenTwo"/>
-  <SyncopeRole_entitlements entitlement="USER_SEARCH" role_id="Search for realm evenTwo"/>
+  <SyncopeRole id="Search for realm evenTwo" entitlements='["USER_READ","USER_SEARCH"]'/>
   <SyncopeRole_Realm role_id="Search for realm evenTwo" realm_id="0679e069-7355-4b20-bd11-a5a0a5453c7c"/>
 
-  <SyncopeRole id="Connector and Resource for realm evenTwo"/>
-  <SyncopeRole_entitlements entitlement="CONNECTOR_READ" role_id="Connector and Resource for realm evenTwo"/>
-  <SyncopeRole_entitlements entitlement="CONNECTOR_UPDATE" role_id="Connector and Resource for realm evenTwo"/>
-  <SyncopeRole_entitlements entitlement="CONNECTOR_DELETE" role_id="Connector and Resource for realm evenTwo"/>
-  <SyncopeRole_entitlements entitlement="CONNECTOR_LIST" role_id="Connector and Resource for realm evenTwo"/>
-  <SyncopeRole_entitlements entitlement="RESOURCE_READ" role_id="Connector and Resource for realm evenTwo"/>
-  <SyncopeRole_entitlements entitlement="RESOURCE_UPDATE" role_id="Connector and Resource for realm evenTwo"/>
-  <SyncopeRole_entitlements entitlement="RESOURCE_DELETE" role_id="Connector and Resource for realm evenTwo"/>
-  <SyncopeRole_entitlements entitlement="RESOURCE_LIST" role_id="Connector and Resource for realm evenTwo"/>
+  <SyncopeRole id="Connector and Resource for realm evenTwo" entitlements='["CONNECTOR_READ","CONNECTOR_UPDATE","CONNECTOR_DELETE","CONNECTOR_LIST","RESOURCE_READ","RESOURCE_UPDATE","RESOURCE_DELETE","RESOURCE_LIST"]'/>
   <SyncopeRole_Realm role_id="Connector and Resource for realm evenTwo"
                      realm_id="0679e069-7355-4b20-bd11-a5a0a5453c7c"/>
 
@@ -801,25 +771,25 @@
                   body="org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate"/>
   <Implementation id="PushJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
                   body="org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate"/>
-  <Task DTYPE="PropagationTask" id="1e697572-b896-484c-ae7f-0c8f63fcbc6c" operation="UPDATE"
-        objectClassName="__ACCOUNT__" resource_id="ws-target-resource-2" anyTypeKind="USER" entityKey="1417acbe-cbf6-4277-9372-e75e04f97000"
-        propagationData='{"attributes":[{"name":"__PASSWORD__","value":[{"readOnly":false,"disposed":false,"encryptedBytes":"m9nh2US0Sa6m+cXccCq0Xw==","base64SHA1Hash":"GFJ69qfjxEOdrmt+9q+0Cw2uz60="}]},{"name":"__NAME__","value":["userId"],"nameValue":"userId"},{"name":"fullname","value":["fullname"]},{"name":"type","value":["type"]}]}'/>
-  <TaskExec id="e58ca1c7-178a-4012-8a71-8aa14eaf0655" task_id="1e697572-b896-484c-ae7f-0c8f63fcbc6c" startDate="2015-12-17 09:40:00" endDate="2015-12-17 09:42:00" status="SUCCESS"/>
-  <Task DTYPE="PropagationTask" id="b8870cfb-3c1e-4fc4-abcb-2559826232e6" operation="CREATE"
-        objectClassName="__ACCOUNT__" resource_id="ws-target-resource-2" anyTypeKind="USER" entityKey="1417acbe-cbf6-4277-9372-e75e04f97000"
-        propagationData='{"attributes":[{"name":"__PASSWORD__","value":[{"readOnly":false,"disposed":false,"encryptedBytes":"m9nh2US0Sa6m+cXccCq0Xw==","base64SHA1Hash":"GFJ69qfjxEOdrmt+9q+0Cw2uz60="}]},{"name":"__NAME__","value":["userId"],"nameValue":"userId"},{"name":"fullname","value":["fullname"]},{"name":"type","value":["type"]}]}'/>
-  <Task DTYPE="PropagationTask" id="316285cc-ae52-4ea2-a33b-7355e189ac3f" operation="DELETE"
-        objectClassName="__ACCOUNT__" resource_id="ws-target-resource-2" anyTypeKind="USER" entityKey="1417acbe-cbf6-4277-9372-e75e04f97000"
-        propagationData='{"attributes":[{"name":"__PASSWORD__","value":[{"readOnly":false,"disposed":false,"encryptedBytes":"m9nh2US0Sa6m+cXccCq0Xw==","base64SHA1Hash":"GFJ69qfjxEOdrmt+9q+0Cw2uz60="}]},{"name":"__NAME__","value":["userId"],"nameValue":"userId"},{"name":"type","value":["type"]}]}'/>
+  <PropagationTask id="1e697572-b896-484c-ae7f-0c8f63fcbc6c" operation="UPDATE"
+                   objectClassName="__ACCOUNT__" resource_id="ws-target-resource-2" anyTypeKind="USER" entityKey="1417acbe-cbf6-4277-9372-e75e04f97000"
+                   propagationData='{"attributes":[{"name":"__PASSWORD__","value":[{"readOnly":false,"disposed":false,"encryptedBytes":"m9nh2US0Sa6m+cXccCq0Xw==","base64SHA1Hash":"GFJ69qfjxEOdrmt+9q+0Cw2uz60="}]},{"name":"__NAME__","value":["userId"],"nameValue":"userId"},{"name":"fullname","value":["fullname"]},{"name":"type","value":["type"]}]}'/>
+  <PropagationTaskExec id="e58ca1c7-178a-4012-8a71-8aa14eaf0655" task_id="1e697572-b896-484c-ae7f-0c8f63fcbc6c" startDate="2015-12-17 09:40:00" endDate="2015-12-17 09:42:00" status="SUCCESS"/>
+  <PropagationTask id="b8870cfb-3c1e-4fc4-abcb-2559826232e6" operation="CREATE"
+                   objectClassName="__ACCOUNT__" resource_id="ws-target-resource-2" anyTypeKind="USER" entityKey="1417acbe-cbf6-4277-9372-e75e04f97000"
+                   propagationData='{"attributes":[{"name":"__PASSWORD__","value":[{"readOnly":false,"disposed":false,"encryptedBytes":"m9nh2US0Sa6m+cXccCq0Xw==","base64SHA1Hash":"GFJ69qfjxEOdrmt+9q+0Cw2uz60="}]},{"name":"__NAME__","value":["userId"],"nameValue":"userId"},{"name":"fullname","value":["fullname"]},{"name":"type","value":["type"]}]}'/>
+  <PropagationTask id="316285cc-ae52-4ea2-a33b-7355e189ac3f" operation="DELETE"
+                   objectClassName="__ACCOUNT__" resource_id="ws-target-resource-2" anyTypeKind="USER" entityKey="1417acbe-cbf6-4277-9372-e75e04f97000"
+                   propagationData='{"attributes":[{"name":"__PASSWORD__","value":[{"readOnly":false,"disposed":false,"encryptedBytes":"m9nh2US0Sa6m+cXccCq0Xw==","base64SHA1Hash":"GFJ69qfjxEOdrmt+9q+0Cw2uz60="}]},{"name":"__NAME__","value":["userId"],"nameValue":"userId"},{"name":"type","value":["type"]}]}'/>
   <!--SYNCOPE-1641 to be purged-->
-  <Task DTYPE="PropagationTask" id="025c956d-ea88-4bd7-9e44-2f35e0aa7055" operation="UPDATE"
-        objectClassName="__ACCOUNT__" resource_id="ws-target-resource-1" anyTypeKind="USER" entityKey="1417acbe-cbf6-4277-9372-e75e04f97000"
-        propagationData='{"attributes":[{"name":"__PASSWORD__","value":[{"readOnly":false,"disposed":false,"encryptedBytes":"m9nh2US0Sa6m+cXccCq0Xw==","base64SHA1Hash":"GFJ69qfjxEOdrmt+9q+0Cw2uz60="}]},{"name":"__NAME__","value":["userId"],"nameValue":"userId"},{"name":"fullname","value":["fullname"]},{"name":"type","value":["type"]}]}'/>
-  <TaskExec id="c3290f8b-caf9-4a85-84fb-fb619b65cd49" task_id="025c956d-ea88-4bd7-9e44-2f35e0aa7055" startDate="2015-12-17 09:40:00" endDate="2015-12-17 09:42:00" status="SUCCESS"/>
-  <Task DTYPE="PullTask" remediation="0" id="c41b9b71-9bfa-4f90-89f2-84787def4c5c" name="CSV (update matching; assign unmatching)" resource_id="resource-csv"
-        destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"
-        pullMode="INCREMENTAL" unmatchingRule="ASSIGN" matchingRule="UPDATE" active="1"
-        jobDelegate_id="PullJobDelegate"/>
+  <PropagationTask id="025c956d-ea88-4bd7-9e44-2f35e0aa7055" operation="UPDATE"
+                   objectClassName="__ACCOUNT__" resource_id="ws-target-resource-1" anyTypeKind="USER" entityKey="1417acbe-cbf6-4277-9372-e75e04f97000"
+                   propagationData='{"attributes":[{"name":"__PASSWORD__","value":[{"readOnly":false,"disposed":false,"encryptedBytes":"m9nh2US0Sa6m+cXccCq0Xw==","base64SHA1Hash":"GFJ69qfjxEOdrmt+9q+0Cw2uz60="}]},{"name":"__NAME__","value":["userId"],"nameValue":"userId"},{"name":"fullname","value":["fullname"]},{"name":"type","value":["type"]}]}'/>
+  <PropagationTaskExec id="c3290f8b-caf9-4a85-84fb-fb619b65cd49" task_id="025c956d-ea88-4bd7-9e44-2f35e0aa7055" startDate="2015-12-17 09:40:00" endDate="2015-12-17 09:42:00" status="SUCCESS"/>
+  <PullTask remediation="0" id="c41b9b71-9bfa-4f90-89f2-84787def4c5c" name="CSV (update matching; assign unmatching)" resource_id="resource-csv"
+            destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"
+            pullMode="INCREMENTAL" unmatchingRule="ASSIGN" matchingRule="UPDATE" active="1"
+            jobDelegate_id="PullJobDelegate"/>
   <AnyTemplatePullTask id="3a6173a9-8c34-4e37-b3b1-0c2ea385fac0"
                        pullTask_id="c41b9b71-9bfa-4f90-89f2-84787def4c5c" anyType_id="USER"
                        template='{"_class":"org.apache.syncope.common.lib.to.UserTO","creator":null,"creationDate":null,"lastModifier":null,"lastChangeDate":null,"key":null,"type":"USER","realm":null,"status":null,"password":null,"token":null,"tokenExpireTime":null,"username":null,"lastLoginDate":null,"changePwdDate":null,"failedLogins":null,"securityQuestion":null,"securityAnswer":null,"auxClasses":["csv"],"derAttrs":[{"schema":"cn","values":[""]}],"virAttrs":[],"resources":["resource-testdb"],"relationships":[],"memberships":[{"groupKey":"f779c0d4-633b-4be5-8f57-32eb478a3ca5","groupName":null}],"dynMemberships":[],"roles":[],"dynRoles":[],"plainAttrs":[{"schema":"ctype","values":["email == &apos;test8@syncope.apache.org&apos;? &apos;TYPE_8&apos;: &apos;TYPE_OTHER&apos;"]}]}'/>
@@ -828,32 +798,32 @@
                        template='{"_class":"org.apache.syncope.common.lib.to.GroupTO","creator":null,"creationDate":null,"lastModifier":null,"lastChangeDate":null,"key":null,"type":"GROUP","realm":null,"status":null,"name":null,"userOwner":null,"groupOwner":null,"udynMembershipCond":null,"auxClasses":[],"derAttrs":[],"virAttrs":[],"resources":[],"plainAttrs":[]}'/>
   <Implementation id="TestSampleJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
                   body="org.apache.syncope.fit.core.reference.TestSampleJobDelegate"/>
-  <Task DTYPE="SchedTask" id="e95555d2-1b09-42c8-b25b-f4c4ec597979" name="SampleJob Task" active="1"
-        jobDelegate_id="TestSampleJobDelegate" cronExpression="0 0 0 1 * ?"/>
+  <SchedTask id="e95555d2-1b09-42c8-b25b-f4c4ec597979" name="SampleJob Task" active="1"
+             jobDelegate_id="TestSampleJobDelegate" cronExpression="0 0 0 1 * ?"/>
   <Implementation id="ExpiredAccessTokenCleanup" type="TASKJOB_DELEGATE" engine="JAVA"
                   body="org.apache.syncope.core.provisioning.java.job.ExpiredAccessTokenCleanup"/>
-  <Task DTYPE="SchedTask" id="89de5014-e3f5-4462-84d8-d97575740baf" name="Access Token Cleanup Task"  active="1"
-        jobDelegate_id="ExpiredAccessTokenCleanup" cronExpression="0 0/5 * * * ?"/>
-  <Task DTYPE="PropagationTask" id="d6c2d6d3-6329-44c1-9187-f1469ead1cfa" operation="UPDATE"
-        objectClassName="__ACCOUNT__" resource_id="ws-target-resource-nopropagation" anyTypeKind="USER" entityKey="1417acbe-cbf6-4277-9372-e75e04f97000"
-        propagationData='{"attributes":[{"name":"__PASSWORD__","value":[{"readOnly":false,"disposed":false,"encryptedBytes":"m9nh2US0Sa6m+cXccCq0Xw==","base64SHA1Hash":"GFJ69qfjxEOdrmt+9q+0Cw2uz60="}]},{"name":"__NAME__","value":["userId"],"nameValue":"userId"},{"name":"fullname","value":["fullname"]},{"name":"type","value":["type"]}]}'/>
-  <TaskExec id="d789462f-e395-424f-bd8e-0db44a93222f" task_id="d6c2d6d3-6329-44c1-9187-f1469ead1cfa" startDate="2015-12-17 09:40:00" endDate="2015-12-17 09:42:00" status="SUCCESS"/>
-  <Task DTYPE="PullTask" remediation="0" id="83f7e85d-9774-43fe-adba-ccd856312994" name="TestDB Task" resource_id="resource-testdb"
-        destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="0" syncStatus="1" pullMode="FULL_RECONCILIATION"
-        unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
+  <SchedTask id="89de5014-e3f5-4462-84d8-d97575740baf" name="Access Token Cleanup Task" active="1"
+             jobDelegate_id="ExpiredAccessTokenCleanup" cronExpression="0 0/5 * * * ?"/>
+  <PropagationTask id="d6c2d6d3-6329-44c1-9187-f1469ead1cfa" operation="UPDATE"
+                   objectClassName="__ACCOUNT__" resource_id="ws-target-resource-nopropagation" anyTypeKind="USER" entityKey="1417acbe-cbf6-4277-9372-e75e04f97000"
+                   propagationData='{"attributes":[{"name":"__PASSWORD__","value":[{"readOnly":false,"disposed":false,"encryptedBytes":"m9nh2US0Sa6m+cXccCq0Xw==","base64SHA1Hash":"GFJ69qfjxEOdrmt+9q+0Cw2uz60="}]},{"name":"__NAME__","value":["userId"],"nameValue":"userId"},{"name":"fullname","value":["fullname"]},{"name":"type","value":["type"]}]}'/>
+  <PropagationTaskExec id="d789462f-e395-424f-bd8e-0db44a93222f" task_id="d6c2d6d3-6329-44c1-9187-f1469ead1cfa" startDate="2015-12-17 09:40:00" endDate="2015-12-17 09:42:00" status="SUCCESS"/>
+  <PullTask remediation="0" id="83f7e85d-9774-43fe-adba-ccd856312994" name="TestDB Task" resource_id="resource-testdb"
+            destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="0" syncStatus="1" pullMode="FULL_RECONCILIATION"
+            unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
   <AnyTemplatePullTask id="6c3f578d-327b-4a7c-8037-6f5ba24eb770" pullTask_id="83f7e85d-9774-43fe-adba-ccd856312994" anyType_id="USER"
                        template='{"_class":"org.apache.syncope.common.lib.to.UserTO","creator":null,"creationDate":null,"lastModifier":null,"lastChangeDate":null,"key":null,"type":"USER","realm":null,"status":null,"password":null,"token":null,"tokenExpireTime":null,"username":null,"lastLoginDate":null,"changePwdDate":null,"failedLogins":null,"securityQuestion":null,"securityAnswer":null,"auxClasses":[],"derAttrs":[],"virAttrs":[],"resources":[],"relationships":[],"memberships":[],"dynMemberships":[],"roles":[],"dynRoles":[],"plainAttrs":[{"schema":"ctype","values":["&apos;type a&apos;"]},{"schema":"userId","values":["&apos;reconciled@syncope.apache.org&apos;"]},{"schema":"fullname","values":["&apos;reconciled fullname&apos;"]},{"schema":"surname","values":["&apos;surname&apos;"]}]}'/>
   <AnyTemplatePullTask id="45b61137-c7c3-49ee-86e0-9efffa75ae68" pullTask_id="83f7e85d-9774-43fe-adba-ccd856312994" anyType_id="GROUP"
                        template='{"_class":"org.apache.syncope.common.lib.to.GroupTO","creator":null,"creationDate":null,"lastModifier":null,"lastChangeDate":null,"key":null,"type":"GROUP","realm":null,"status":null,"name":null,"userOwner":null,"groupOwner":null,"udynMembershipCond":null,"auxClasses":[],"derAttrs":[],"virAttrs":[],"resources":[],"plainAttrs":[]}'/>
-  <Task DTYPE="PullTask" remediation="0" id="81d88f73-d474-4450-9031-605daa4e313f" name="TestDB2 Task" resource_id="resource-testdb2"
-        destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="0" syncStatus="1" pullMode="FULL_RECONCILIATION"
-        unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
-  <Task DTYPE="PullTask" remediation="0" id="7c2242f4-14af-4ab5-af31-cdae23783655" name="TestDB Pull Task" resource_id="resource-db-pull"
-        destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" pullMode="FULL_RECONCILIATION" performCreate="1" performDelete="1" performUpdate="1" syncStatus="0"
-        unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
-  <Task DTYPE="PullTask" remediation="0" id="1e419ca4-ea81-4493-a14f-28b90113686d" name="LDAP Pull Task" resource_id="resource-ldap"
-        destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" pullMode="FULL_RECONCILIATION" performCreate="1" performDelete="1" performUpdate="1" syncStatus="0"
-        unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
+  <PullTask remediation="0" id="81d88f73-d474-4450-9031-605daa4e313f" name="TestDB2 Task" resource_id="resource-testdb2"
+            destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="0" syncStatus="1" pullMode="FULL_RECONCILIATION"
+            unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
+  <PullTask remediation="0" id="7c2242f4-14af-4ab5-af31-cdae23783655" name="TestDB Pull Task" resource_id="resource-db-pull"
+            destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" pullMode="FULL_RECONCILIATION" performCreate="1" performDelete="1" performUpdate="1" syncStatus="0"
+            unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
+  <PullTask remediation="0" id="1e419ca4-ea81-4493-a14f-28b90113686d" name="LDAP Pull Task" resource_id="resource-ldap"
+            destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" pullMode="FULL_RECONCILIATION" performCreate="1" performDelete="1" performUpdate="1" syncStatus="0"
+            unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
   <AnyTemplatePullTask id="df655a2a-40c0-43b1-a157-3f4988802f58" pullTask_id="1e419ca4-ea81-4493-a14f-28b90113686d" anyType_id="USER"
                        template='{"_class":"org.apache.syncope.common.lib.to.UserTO","creator":null,"creationDate":null,"lastModifier":null,"lastChangeDate":null,"key":null,"type":"USER","realm":"&apos;/&apos; + title","status":null,"password":null,"token":null,"tokenExpireTime":null,"username":null,"lastLoginDate":null,"changePwdDate":null,"failedLogins":null,"securityQuestion":null,"securityAnswer":null,"auxClasses":["minimal group"],"derAttrs":[],"virAttrs":[{"schema":"virtualReadOnly","values":[""]}],"resources":["resource-ldap"],"roles":[],"dynRoles":[],"relationships":[],"memberships":[],"dynMemberships":[],"plainAttrs":[]}'/>
   <AnyTemplatePullTask id="fda22ff3-98f3-42e4-a2ae-cd9a28282d57" pullTask_id="1e419ca4-ea81-4493-a14f-28b90113686d" anyType_id="GROUP"
@@ -861,98 +831,98 @@
   <Implementation id="LDAPMembershipPullActions" type="PULL_ACTIONS"  engine="JAVA"
                   body="org.apache.syncope.core.provisioning.java.pushpull.LDAPMembershipPullActions"/>
   <PullTaskAction task_id="1e419ca4-ea81-4493-a14f-28b90113686d" implementation_id="LDAPMembershipPullActions"/>
-  <Task DTYPE="PullTask" remediation="0" id="38abbf9e-a1a3-40a1-a15f-7d0ac02f47f1" name="VirAttrCache test" resource_id="resource-csv"
-        destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="0" performUpdate="1" performDelete="0" syncStatus="0" pullMode="FULL_RECONCILIATION"
-        unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
-  <Task DTYPE="PushTask" id="af558be4-9d2f-4359-bf85-a554e6e90be1" name="Export on resource-testdb2" resource_id="resource-testdb2"
-        sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
-        performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"         
-        unmatchingRule="ASSIGN" matchingRule="IGNORE" active="1" jobDelegate_id="PushJobDelegate"/>  
+  <PullTask remediation="0" id="38abbf9e-a1a3-40a1-a15f-7d0ac02f47f1" name="VirAttrCache test" resource_id="resource-csv"
+            destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="0" performUpdate="1" performDelete="0" syncStatus="0" pullMode="FULL_RECONCILIATION"
+            unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
+  <PushTask id="af558be4-9d2f-4359-bf85-a554e6e90be1" name="Export on resource-testdb2" resource_id="resource-testdb2"
+            sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
+            performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"         
+            unmatchingRule="ASSIGN" matchingRule="IGNORE" active="1" jobDelegate_id="PushJobDelegate"/>  
   <PushTaskAnyFilter id="1fdcff65-765f-4a6e-98a7-13ef7cca47e2" anyType_id="USER" pushTask_id="af558be4-9d2f-4359-bf85-a554e6e90be1" fiql="surname==Vivaldi"/>
   <PushTaskAnyFilter id="3b564c51-5d64-48b3-8da5-fd4ebc10e0a8" anyType_id="GROUP" pushTask_id="af558be4-9d2f-4359-bf85-a554e6e90be1" fiql="name==_NO_ONE_"/>
-  <Task DTYPE="PushTask" id="97f327b6-2eff-4d35-85e8-d581baaab855" name="Export on resource-testdb2" resource_id="resource-testdb2"
-        sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
-        performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"         
-        unmatchingRule="PROVISION" matchingRule="IGNORE" active="1" jobDelegate_id="PushJobDelegate"/>
+  <PushTask id="97f327b6-2eff-4d35-85e8-d581baaab855" name="Export on resource-testdb2" resource_id="resource-testdb2"
+            sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
+            performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"         
+            unmatchingRule="PROVISION" matchingRule="IGNORE" active="1" jobDelegate_id="PushJobDelegate"/>
   <PushTaskAnyFilter id="199efd21-5e89-46ac-95de-f47e9d0569fc" anyType_id="USER" pushTask_id="97f327b6-2eff-4d35-85e8-d581baaab855" fiql="surname==Bellini"/>
   <PushTaskAnyFilter id="7672a167-77d6-4639-8b1d-0af561293c7d" anyType_id="GROUP" pushTask_id="97f327b6-2eff-4d35-85e8-d581baaab855" fiql="name==_NO_ONE_"/>
-  <Task DTYPE="PushTask" id="03aa2a04-4881-4573-9117-753f81b04865" name="Export on resource-testdb2" resource_id="resource-testdb2"
-        sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
-        performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
-        unmatchingRule="UNLINK" matchingRule="IGNORE" active="1" jobDelegate_id="PushJobDelegate"/>
+  <PushTask id="03aa2a04-4881-4573-9117-753f81b04865" name="Export on resource-testdb2" resource_id="resource-testdb2"
+            sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
+            performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
+            unmatchingRule="UNLINK" matchingRule="IGNORE" active="1" jobDelegate_id="PushJobDelegate"/>
   <PushTaskAnyFilter id="39a11ba6-397a-4c94-8bfe-1f4f757d6501" anyType_id="USER" pushTask_id="03aa2a04-4881-4573-9117-753f81b04865" fiql="surname==Puccini"/>
   <PushTaskAnyFilter id="5bd7501e-8a18-4fbd-a3fe-a1e731ba95db" anyType_id="GROUP" pushTask_id="03aa2a04-4881-4573-9117-753f81b04865" fiql="name==_NO_ONE_"/>
-  <Task DTYPE="PushTask" id="5e5f7c7e-9de7-4c6a-99f1-4df1af959807" name="Export on resource-testdb2" resource_id="resource-testdb2"
-        sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
-        performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"         
-        unmatchingRule="IGNORE" matchingRule="IGNORE" active="1" jobDelegate_id="PushJobDelegate"/>
+  <PushTask id="5e5f7c7e-9de7-4c6a-99f1-4df1af959807" name="Export on resource-testdb2" resource_id="resource-testdb2"
+            sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
+            performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"         
+            unmatchingRule="IGNORE" matchingRule="IGNORE" active="1" jobDelegate_id="PushJobDelegate"/>
   <PushTaskAnyFilter id="0d0371a3-5772-4b4c-ad14-139adf1d346a" anyType_id="USER" pushTask_id="5e5f7c7e-9de7-4c6a-99f1-4df1af959807" fiql="surname==Verdi"/>
   <PushTaskAnyFilter id="2e7488ae-a2fc-4657-a93b-159b8433c0e7" anyType_id="GROUP" pushTask_id="5e5f7c7e-9de7-4c6a-99f1-4df1af959807" fiql="name==_NO_ONE_"/>
-  <Task DTYPE="PushTask" id="0bc11a19-6454-45c2-a4e3-ceef84e5d79b" name="Export on resource-testdb2" resource_id="resource-testdb2"
-        sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
-        performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
-        unmatchingRule="ASSIGN" matchingRule="UPDATE" active="1" jobDelegate_id="PushJobDelegate"/>
+  <PushTask id="0bc11a19-6454-45c2-a4e3-ceef84e5d79b" name="Export on resource-testdb2" resource_id="resource-testdb2"
+            sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
+            performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
+            unmatchingRule="ASSIGN" matchingRule="UPDATE" active="1" jobDelegate_id="PushJobDelegate"/>
   <PushTaskAnyFilter id="41bf22fe-a014-41af-9a75-402b987eb433" anyType_id="USER" pushTask_id="0bc11a19-6454-45c2-a4e3-ceef84e5d79b" fiql="username==_NO_ONE_"/>
   <PushTaskAnyFilter id="fa983fde-795e-4c89-a6f7-1ccd80a8adeb" anyType_id="GROUP" pushTask_id="0bc11a19-6454-45c2-a4e3-ceef84e5d79b" fiql="name==_NO_ONE_"/>
-  <Task DTYPE="PushTask" id="ec674143-480a-4816-98ad-b61fa090821e" name="Export on resource-testdb2" resource_id="resource-testdb2"
-        sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
-        performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
-        unmatchingRule="IGNORE" matchingRule="DEPROVISION" active="1" jobDelegate_id="PushJobDelegate"/>
+  <PushTask id="ec674143-480a-4816-98ad-b61fa090821e" name="Export on resource-testdb2" resource_id="resource-testdb2"
+            sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
+            performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
+            unmatchingRule="IGNORE" matchingRule="DEPROVISION" active="1" jobDelegate_id="PushJobDelegate"/>
   <PushTaskAnyFilter id="e238a6dc-0b04-46cf-9bfa-be68bd9f2da0" anyType_id="USER" pushTask_id="ec674143-480a-4816-98ad-b61fa090821e" fiql="surname==Verdi"/>
   <PushTaskAnyFilter id="0eaa643e-0add-4c46-8273-539f9d6abec5" anyType_id="GROUP" pushTask_id="ec674143-480a-4816-98ad-b61fa090821e" fiql="name==_NO_ONE_"/>
-  <Task DTYPE="PushTask" id="c46edc3a-a18b-4af2-b707-f4a415507496" name="Export on resource-testdb2" resource_id="resource-testdb2"
-        sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
-        performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
-        unmatchingRule="IGNORE" matchingRule="UNASSIGN" active="1" jobDelegate_id="PushJobDelegate"/>
+  <PushTask id="c46edc3a-a18b-4af2-b707-f4a415507496" name="Export on resource-testdb2" resource_id="resource-testdb2"
+            sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
+            performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
+            unmatchingRule="IGNORE" matchingRule="UNASSIGN" active="1" jobDelegate_id="PushJobDelegate"/>
   <PushTaskAnyFilter id="335b4f11-589a-44c5-80b0-ba94892f0c62" anyType_id="USER" pushTask_id="c46edc3a-a18b-4af2-b707-f4a415507496" fiql="surname==Rossini"/>
   <PushTaskAnyFilter id="b32eecc2-aa4f-43c6-a501-a692c3e93113" anyType_id="GROUP" pushTask_id="c46edc3a-a18b-4af2-b707-f4a415507496" fiql="name==_NO_ONE_"/>
-  <Task DTYPE="PushTask" id="51318433-cce4-4f71-8f45-9534b6c9c819" name="Export on resource-testdb2" resource_id="resource-testdb2"
-        sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
-        performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
-        unmatchingRule="IGNORE" matchingRule="LINK" active="1" jobDelegate_id="PushJobDelegate"/>
+  <PushTask id="51318433-cce4-4f71-8f45-9534b6c9c819" name="Export on resource-testdb2" resource_id="resource-testdb2"
+            sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
+            performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
+            unmatchingRule="IGNORE" matchingRule="LINK" active="1" jobDelegate_id="PushJobDelegate"/>
   <PushTaskAnyFilter id="9f974a0d-87d8-4cae-9ea9-1fc245bc1dbf" anyType_id="USER" pushTask_id="51318433-cce4-4f71-8f45-9534b6c9c819" fiql="surname==Verdi"/>
   <PushTaskAnyFilter id="0dc46ba4-1270-4fa9-b3e1-79f940d4308f" anyType_id="GROUP" pushTask_id="51318433-cce4-4f71-8f45-9534b6c9c819" fiql="name==_NO_ONE_"/>
-  <Task DTYPE="PushTask" id="24b1be9c-7e3b-443a-86c9-798ebce5eaf2" name="Export on resource-testdb2" resource_id="resource-testdb2"
-        sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
-        performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
-        unmatchingRule="IGNORE" matchingRule="UNLINK" active="1" jobDelegate_id="PushJobDelegate"/>
+  <PushTask id="24b1be9c-7e3b-443a-86c9-798ebce5eaf2" name="Export on resource-testdb2" resource_id="resource-testdb2"
+            sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
+            performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
+            unmatchingRule="IGNORE" matchingRule="UNLINK" active="1" jobDelegate_id="PushJobDelegate"/>
   <PushTaskAnyFilter id="3aa3b0b8-7469-4859-89d5-476ae5915101" anyType_id="USER" pushTask_id="24b1be9c-7e3b-443a-86c9-798ebce5eaf2" fiql="surname==Verdi"/>
   <PushTaskAnyFilter id="f054810e-6842-4017-8f60-5b4031fa2c72" anyType_id="GROUP" pushTask_id="24b1be9c-7e3b-443a-86c9-798ebce5eaf2" fiql="name==_NO_ONE_"/>
-  <Task DTYPE="PushTask" id="375c7b7f-9e3a-4833-88c9-b7787b0a69f2" name="Export on resource-testdb2" resource_id="resource-testdb2"
-        sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
-        performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
-        unmatchingRule="IGNORE" matchingRule="UPDATE"  active="1" jobDelegate_id="PushJobDelegate"/>
+  <PushTask id="375c7b7f-9e3a-4833-88c9-b7787b0a69f2" name="Export on resource-testdb2" resource_id="resource-testdb2"
+            sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
+            performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
+            unmatchingRule="IGNORE" matchingRule="UPDATE"  active="1" jobDelegate_id="PushJobDelegate"/>
   <PushTaskAnyFilter id="95f047fc-1a8a-45f4-b56c-6e04d8ca5567" anyType_id="USER" pushTask_id="375c7b7f-9e3a-4833-88c9-b7787b0a69f2" fiql="surname==Verdi"/>
   <PushTaskAnyFilter id="013a4298-4b14-4f8b-9f59-191c2d53dbd8" anyType_id="GROUP" pushTask_id="375c7b7f-9e3a-4833-88c9-b7787b0a69f2" fiql="name==_NO_ONE_"/>
-  <Task DTYPE="PushTask" id="fd905ba5-9d56-4f51-83e2-859096a67b75" name="Export on resource-ldap" resource_id="resource-ldap"
-        sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
-        performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
-        unmatchingRule="ASSIGN" matchingRule="UNLINK" active="1" jobDelegate_id="PushJobDelegate"/>
+  <PushTask id="fd905ba5-9d56-4f51-83e2-859096a67b75" name="Export on resource-ldap" resource_id="resource-ldap"
+            sourceRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
+            performCreate="1" performUpdate="1" performDelete="1" syncStatus="1"        
+            unmatchingRule="ASSIGN" matchingRule="UNLINK" active="1" jobDelegate_id="PushJobDelegate"/>
   <PushTaskAnyFilter id="30842acc-f2dd-4d47-b359-20db06c30803" anyType_id="USER" pushTask_id="fd905ba5-9d56-4f51-83e2-859096a67b75" fiql="username==_NO_ONE_"/>
   <PushTaskAnyFilter id="9e4c0233-440e-4b5b-9563-11ec0f55a334" anyType_id="GROUP" pushTask_id="fd905ba5-9d56-4f51-83e2-859096a67b75" fiql="name==citizen"/>
-  <Task DTYPE="PullTask" remediation="0" id="986867e2-993b-430e-8feb-aa9abb4c1dcd" name="CSV Task (update matching; provision unmatching)" resource_id="resource-csv"
-        destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="1" syncStatus="1" pullMode="INCREMENTAL"
-        unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
+  <PullTask remediation="0" id="986867e2-993b-430e-8feb-aa9abb4c1dcd" name="CSV Task (update matching; provision unmatching)" resource_id="resource-csv"
+            destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="1" syncStatus="1" pullMode="INCREMENTAL"
+            unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
   <AnyTemplatePullTask id="8bc41ba1-cc1d-4ee0-bb43-61cd148b414f" pullTask_id="986867e2-993b-430e-8feb-aa9abb4c1dcd" anyType_id="USER"
                        template='{"_class":"org.apache.syncope.common.lib.to.UserTO","creator":null,"creationDate":null,"lastModifier":null,"lastChangeDate":null,"key":null,"type":"USER","realm":null,"status":null,"password":null,"token":null,"tokenExpireTime":null,"username":null,"lastLoginDate":null,"changePwdDate":null,"failedLogins":null,"securityQuestion":null,"securityAnswer":null,"auxClasses":[],"derAttrs":[],"virAttrs":[],"resources":["resource-testdb"],"roles":[],"dynRoles":[],"relationships":[],"memberships":[],"dynMemberships":[],"plainAttrs":[{"schema":"firstname","values":[""]},{"schema":"userId","values":["&apos;test&apos;"]},{"schema":"fullname","values":["&apos;test&apos;"]},{"schema":"surname","values":["&apos;test&apos;"]}]}'/>
   <AnyTemplatePullTask id="9af0e343-8a37-42d2-9bc7-6e2e3b103219" pullTask_id="986867e2-993b-430e-8feb-aa9abb4c1dcd" anyType_id="GROUP"
                        template='{"_class":"org.apache.syncope.common.lib.to.GroupTO","creator":null,"creationDate":null,"lastModifier":null,"lastChangeDate":null,"key":null,"type":"GROUP","realm":null,"status":null,"name":null,"userOwner":null,"groupOwner":null,"udynMembershipCond":null,"auxClasses":[],"derAttrs":[],"virAttrs":[],"resources":[],"plainAttrs":[]}'/>
-  <Task DTYPE="PullTask" remediation="0" id="feae4e57-15ca-40d9-b973-8b9015efca49" name="CSV (unlink matching; ignore unmatching)" resource_id="resource-csv"
-        destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="1" syncStatus="1" pullMode="FULL_RECONCILIATION"
-        unmatchingRule="IGNORE" matchingRule="UNLINK" active="1" jobDelegate_id="PullJobDelegate"/>
-  <Task DTYPE="PullTask" remediation="0" id="55d5e74b-497e-4bc0-9156-73abef4b9adc" name="CSV (ignore matching; assign unmatching)" resource_id="resource-csv"
-        destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="1" syncStatus="1" pullMode="FULL_RECONCILIATION"
-        unmatchingRule="ASSIGN" matchingRule="IGNORE" active="1" jobDelegate_id="PullJobDelegate"/>
-  <Task DTYPE="PropagationTask" id="0f618183-17ce-48bc-80bc-cc535f38983a" operation="CREATE"
-        objectClassName="__ACCOUNT__" resource_id="resource-testdb" anyTypeKind="USER" entityKey="1417acbe-cbf6-4277-9372-e75e04f97000"
-        propagationData='{"attributes":[{"name":"__PASSWORD__","value":[{"readOnly":false,"disposed":false,"encryptedBytes":"m9nh2US0Sa6m+cXccCq0Xw==","base64SHA1Hash":"GFJ69qfjxEOdrmt+9q+0Cw2uz60="}]},{"name":"__NAME__","value":["userId"],"nameValue":"userId"},{"name":"fullname","value":["fullname"]},{"name":"type","value":["type"]}]}'/>
-  <Task DTYPE="PullTask" remediation="0" id="30cfd653-257b-495f-8665-281281dbcb3d" name="Scripted SQL" resource_id="resource-db-scripted"
-        destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="0" syncStatus="0" pullMode="INCREMENTAL"
-        unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
+  <PullTask remediation="0" id="feae4e57-15ca-40d9-b973-8b9015efca49" name="CSV (unlink matching; ignore unmatching)" resource_id="resource-csv"
+            destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="1" syncStatus="1" pullMode="FULL_RECONCILIATION"
+            unmatchingRule="IGNORE" matchingRule="UNLINK" active="1" jobDelegate_id="PullJobDelegate"/>
+  <PullTask remediation="0" id="55d5e74b-497e-4bc0-9156-73abef4b9adc" name="CSV (ignore matching; assign unmatching)" resource_id="resource-csv"
+            destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="1" syncStatus="1" pullMode="FULL_RECONCILIATION"
+            unmatchingRule="ASSIGN" matchingRule="IGNORE" active="1" jobDelegate_id="PullJobDelegate"/>
+  <PropagationTask id="0f618183-17ce-48bc-80bc-cc535f38983a" operation="CREATE"
+                   objectClassName="__ACCOUNT__" resource_id="resource-testdb" anyTypeKind="USER" entityKey="1417acbe-cbf6-4277-9372-e75e04f97000"
+                   propagationData='{"attributes":[{"name":"__PASSWORD__","value":[{"readOnly":false,"disposed":false,"encryptedBytes":"m9nh2US0Sa6m+cXccCq0Xw==","base64SHA1Hash":"GFJ69qfjxEOdrmt+9q+0Cw2uz60="}]},{"name":"__NAME__","value":["userId"],"nameValue":"userId"},{"name":"fullname","value":["fullname"]},{"name":"type","value":["type"]}]}'/>
+  <PullTask remediation="0" id="30cfd653-257b-495f-8665-281281dbcb3d" name="Scripted SQL" resource_id="resource-db-scripted"
+            destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="0" syncStatus="0" pullMode="INCREMENTAL"
+            unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
   <Implementation id="ExpiredBatchCleanup" type="TASKJOB_DELEGATE" engine="JAVA"
                   body="org.apache.syncope.core.provisioning.java.job.ExpiredBatchCleanup"/>
-  <Task DTYPE="SchedTask" id="8ea0ea51-ce08-4fe3-a0c8-c281b31b5893" name="Expired Batch Operations Cleanup Task"  active="1"
-        jobDelegate_id="ExpiredBatchCleanup" cronExpression="0 0/5 * * * ?"/>
+  <SchedTask id="8ea0ea51-ce08-4fe3-a0c8-c281b31b5893" name="Expired Batch Operations Cleanup Task" active="1"
+             jobDelegate_id="ExpiredBatchCleanup" cronExpression="0 0/5 * * * ?"/>
 
   <MailTemplate id="requestPasswordReset"
                 textTemplate="Hi,
@@ -1054,26 +1024,20 @@
 
   <Notification id="e00945b5-1184-4d43-8e45-4318a8dcdfd4" active="1" recipientAttrName="email" selfAsRecipient="1" 
                 sender="admin@syncope.apache.org" subject="Password Reset request" template_id="requestPasswordReset" 
-                traceLevel="FAILURES"/> 
+                traceLevel="FAILURES" events='["[CUSTOM]:[]:[]:[requestPasswordReset]:[SUCCESS]"]'/> 
   <AnyAbout id="a328f2e6-25e9-4cc1-badf-7425d7be4b39" anyType_id="USER" notification_id="e00945b5-1184-4d43-8e45-4318a8dcdfd4" anyType_filter="token!=$null"/>
-  <Notification_events notification_id="e00945b5-1184-4d43-8e45-4318a8dcdfd4" event="[CUSTOM]:[]:[]:[requestPasswordReset]:[SUCCESS]"/>
-  <Task DTYPE="NotificationTask" id="e1e520f0-2cbd-4e11-9a89-ea58a0f957e7" notification_id="e00945b5-1184-4d43-8e45-4318a8dcdfd4"
-        sender="admin@prova.org" subject="Notification for SYNCOPE-81" executed="0"
-        textBody="NOTIFICATION-81" htmlBody="NOTIFICATION-81" traceLevel="ALL"/>
-  <NotificationTask_recipients notificationTask_id="e1e520f0-2cbd-4e11-9a89-ea58a0f957e7" address="recipient@prova.org"/>  
+  <NotificationTask id="e1e520f0-2cbd-4e11-9a89-ea58a0f957e7" notification_id="e00945b5-1184-4d43-8e45-4318a8dcdfd4"
+                    sender="admin@prova.org" subject="Notification for SYNCOPE-81" executed="0"
+                    textBody="NOTIFICATION-81" htmlBody="NOTIFICATION-81" traceLevel="ALL" recipients='["recipient@prova.org"]'/>
   
   <Notification id="bef0c250-e8a7-4848-bb63-2564fc409ce2" active="1" recipientAttrName="email" selfAsRecipient="1" 
                 sender="admin@syncope.apache.org" subject="Password Reset successful" template_id="confirmPasswordReset" 
-                traceLevel="FAILURES"/> 
-  <Notification_events notification_id="bef0c250-e8a7-4848-bb63-2564fc409ce2" event="[CUSTOM]:[]:[]:[confirmPasswordReset]:[SUCCESS]"/>
+                traceLevel="FAILURES" events='["[CUSTOM]:[]:[]:[confirmPasswordReset]:[SUCCESS]"]'/> 
 
   <Notification id="9e2b911c-25de-4c77-bcea-b86ed9451050" sender="test@syncope.apache.org" subject="Test subject" template_id="test" selfAsRecipient="0" 
-                traceLevel="FAILURES"
-                recipientsFIQL="$groups==7"
-                recipientAttrName="email" active="1"/>
+                traceLevel="FAILURES" recipientsFIQL="$groups==7" recipientAttrName="email" active="1"
+                events='["[CUSTOM]:[]:[]:[unexisting1]:[FAILURE]", "[CUSTOM]:[]:[]:[unexisting2]:[SUCCESS]"]'/>
   <AnyAbout id="2e2ee845-2abf-43c6-b543-49243a84e2f1" anyType_id="USER" notification_id="9e2b911c-25de-4c77-bcea-b86ed9451050" anyType_filter="fullname==*o*;fullname==*i*"/>
-  <Notification_events notification_id="9e2b911c-25de-4c77-bcea-b86ed9451050" event="[CUSTOM]:[]:[]:[unexisting1]:[FAILURE]"/>
-  <Notification_events notification_id="9e2b911c-25de-4c77-bcea-b86ed9451050" event="[CUSTOM]:[]:[]:[unexisting2]:[SUCCESS]"/>
 
   <ReportTemplate id="empty"/>  
   <ReportTemplate id="sample"
@@ -2189,22 +2153,7 @@
             logout="0" csrf="1" routeType="PROTECTED"
             predicates="[{&quot;cond&quot;:null,&quot;factory&quot;:&quot;METHOD&quot;,&quot;args&quot;:&quot;GET&quot;}]"/>
 
-  <SyncopeRole id="GROUP_OWNER"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_SEARCH"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_CREATE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_UPDATE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_DELETE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPECLASS_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPE_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPECLASS_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="RELATIONSHIPTYPE_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPE_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="REALM_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_SEARCH"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_UPDATE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_DELETE"/>
+  <SyncopeRole id="GROUP_OWNER" entitlements='["USER_SEARCH","USER_READ","USER_CREATE","USER_UPDATE","USER_DELETE","ANYTYPECLASS_READ","ANYTYPE_LIST","ANYTYPECLASS_LIST","RELATIONSHIPTYPE_LIST","ANYTYPE_READ","REALM_LIST","GROUP_SEARCH","GROUP_READ","GROUP_UPDATE","GROUP_DELETE"]'/>
 
   <AuditConf id="syncope.audit.[LOGIC]:[SyncopeLogic]:[]:[isSelfRegAllowed]:[SUCCESS]" active="1"/>
 
diff --git a/core/persistence-jpa/src/test/resources/domains/TwoContent.xml b/core/persistence-jpa/src/test/resources/domains/TwoContent.xml
index 68ac4bd..8724010 100644
--- a/core/persistence-jpa/src/test/resources/domains/TwoContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/TwoContent.xml
@@ -41,8 +41,8 @@
 
   <Implementation id="ExpiredAccessTokenCleanup" type="TASKJOB_DELEGATE" engine="JAVA"
                   body="org.apache.syncope.core.provisioning.java.job.ExpiredAccessTokenCleanup"/>
-  <Task DTYPE="SchedTask" id="89de5014-e3f5-4462-84d8-d97575740baf" name="Access Token Cleanup Task"  active="1"
-        jobDelegate_id="ExpiredAccessTokenCleanup" cronExpression="0 0/5 * * * ?"/>
+  <SchedTask id="89de5014-e3f5-4462-84d8-d97575740baf" name="Access Token Cleanup Task"  active="1"
+             jobDelegate_id="ExpiredAccessTokenCleanup" cronExpression="0 0/5 * * * ?"/>
   
   <!-- Password reset notifications -->
   <MailTemplate id="requestPasswordReset"
@@ -85,14 +85,12 @@
 
   <Notification id="c74b4616-9c63-4350-b4bf-ae0077b1ae6a" active="1" recipientAttrName="email" selfAsRecipient="1" 
                 sender="admin@syncope.apache.org" subject="Password Reset request" template_id="requestPasswordReset" 
-                traceLevel="FAILURES"/> 
+                traceLevel="FAILURES" events='["[CUSTOM]:[]:[]:[requestPasswordReset]:[SUCCESS]"]'/> 
   <AnyAbout id="0d4e37a1-a4f4-4865-afcb-4be01da3da53" anyType_id="USER" notification_id="c74b4616-9c63-4350-b4bf-ae0077b1ae6a" anyType_filter="token!=$null"/>
-  <Notification_events notification_id="c74b4616-9c63-4350-b4bf-ae0077b1ae6a" event="[CUSTOM]:[]:[]:[requestPasswordReset]:[SUCCESS]"/>
   
   <Notification id="71769807-7f74-4dc3-ba61-e4a7a00eb8ad" active="1" recipientAttrName="email" selfAsRecipient="1" 
                 sender="admin@syncope.apache.org" subject="Password Reset successful" template_id="confirmPasswordReset" 
-                traceLevel="FAILURES"/> 
-  <Notification_events notification_id="71769807-7f74-4dc3-ba61-e4a7a00eb8ad" event="[CUSTOM]:[]:[]:[confirmPasswordReset]:[SUCCESS]"/>
+                traceLevel="FAILURES" events='["[CUSTOM]:[]:[]:[confirmPasswordReset]:[SUCCESS]"]'/> 
 
   <ConnInstance id="b7ea96c3-c633-488b-98a0-b52ac35850f7" bundleName="net.tirasa.connid.bundles.ldap" displayName="LDAP"
                 adminRealm_id="ea696a4f-e77a-4ef1-be67-8f8093bc8686"
@@ -101,5 +99,6 @@
                 version="${connid.ldap.version}" 
                 jsonConf='[{"schema":{"name":"synchronizePasswords","displayName":"Enable Password Synchronization","helpMessage":"If true, the connector will synchronize passwords. The Password Capture Plugin needs to be installed for password synchronization to work.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"maintainLdapGroupMembership","displayName":"Maintain LDAP Group Membership","helpMessage":"When enabled and a user is renamed or deleted, update any LDAP groups to which the user belongs to reflect the new name. Otherwise, the LDAP resource must maintain referential integrity with respect to group membership.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["true"]},{"schema":{"name":"host","displayName":"Host","helpMessage":"The name or IP address of the host where the LDAP server is running.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["localhost"]},{"schema":{"name":"passwordHashAlgorithm","displayName":"Password Hash Algorithm","helpMessage":"Indicates the algorithm that the Identity system should use to hash the password. Currently supported values are SSHA, SHA, SSHA1, and SHA1. A blank value indicates that the system will not hash passwords. This will cause cleartext passwords to be stored in LDAP unless the LDAP server performs the hash (Netscape Directory Server and iPlanet Directory Server do).","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["SHA"]},{"schema":{"name":"port","displayName":"TCP Port","helpMessage":"TCP/IP port number used to communicate with the LDAP server.","type":"int","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[1389]},{"schema":{"name":"vlvSortAttribute","displayName":"VLV Sort Attribute","helpMessage":"Specify the sort attribute to use for VLV indexes on the resource.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"statusManagementClass","displayName":"Status management class ","helpMessage":"Class to be used to manage enabled/disabled status. If no class is specified then identity status management wont be possible.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["net.tirasa.connid.bundles.ldap.commons.AttributeStatusManagement"]},{"schema":{"name":"accountObjectClasses","displayName":"Account Object Classes","helpMessage":"The object class or classes that will be used when creating new user objects in the LDAP tree. When entering more than one object class, each entry should be on its own line; do not use commas or semi-colons to separate multiple object classes. Some object classes may require that you specify all object classes in the class hierarchy.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["inetOrgPerson"]},{"schema":{"name":"accountUserNameAttributes","displayName":"Account User Name Attributes","helpMessage":"Attribute or attributes which holds the account user name. They will be used when authenticating to find the LDAP entry for the user name to authenticate.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["uid"]},{"schema":{"name":"baseContextsToSynchronize","displayName":"Base Contexts to Synchronize","helpMessage":"One or more starting points in the LDAP tree that will be used to determine if a change should be synchronized. The base contexts attribute will be used to synchronize a change if this property is not set.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["ou=people,o=isp","ou=groups,o=isp"]},{"schema":{"name":"accountSynchronizationFilter","displayName":"LDAP Filter for Accounts to Synchronize","helpMessage":"An optional LDAP filter for the objects to synchronize. Because the change log is for all objects, this filter updates only objects that match the specified filter. If you specify a filter, an object will be synchronized only if it matches the filter and includes a synchronized object class.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"removeLogEntryObjectClassFromFilter","displayName":"Remove Log Entry Object Class from Filter","helpMessage":"If this property is set (the default), the filter used to fetch change log entries does not contain the \"changeLogEntry\" object class, expecting that there are no entries of other object types in the change log.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"passwordDecryptionKey","displayName":"Password Decryption Key","helpMessage":"The key to decrypt passwords with when performing password synchronization.","type":"org.identityconnectors.common.security.GuardedByteArray","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"readSchema","displayName":"Read Schema","helpMessage":"If true, the connector will read the schema from the server. If false, the connector will provide a default schema based on the object classes in the configuration. This property must be true in order to use extended object classes.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"ssl","displayName":"SSL","helpMessage":"Select the check box to connect to the LDAP server using SSL.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"passwordAttributeToSynchronize","displayName":"Password Attribute to Synchronize","helpMessage":"The name of the password attribute to synchronize when performing password synchronization.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"accountSearchFilter","displayName":"LDAP Filter for Retrieving Accounts","helpMessage":"An optional LDAP filter to control which accounts are returned from the LDAP resource. If no filter is specified, only accounts that include all specified object classes are returned.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["uid=*"]},{"schema":{"name":"passwordDecryptionInitializationVector","displayName":"Password Decryption Initialization Vector","helpMessage":"The initialization vector to decrypt passwords with when performing password synchronization.","type":"org.identityconnectors.common.security.GuardedByteArray","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"groupMemberAttribute","displayName":"Group Member Attribute","helpMessage":"The name of the group attribute that will be updated with the distinguished name of the user when the user is added to the group.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"failover","displayName":"Failover Servers","helpMessage":"List all servers that should be used for failover in case the preferred server fails. If the preferred server fails, JNDI will connect to the next available server in the list. List all servers in the form of \"ldap://ldap.example.com:389/\", which follows the standard LDAP v3 URLs described in RFC 2255. Only the host and port parts of the URL are relevant in this setting.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"modifiersNamesToFilterOut","displayName":"Filter Out Changes By","helpMessage":"The names (DNs) of directory administrators to filter from the changes. Changes with the attribute \"modifiersName\" that match entries in this list will be filtered out. The standard value is the administrator name used by this adapter, to prevent loops. Entries should be of the format \"cn=Directory Manager\".","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"groupNameAttributes","displayName":"Group Name Attributes","helpMessage":"Attribute or attributes which holds the group name.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["cn"]},{"schema":{"name":"uidAttribute","displayName":"Uid Attribute","helpMessage":"The name of the LDAP attribute which is mapped to the Uid attribute.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["cn"]},{"schema":{"name":"respectResourcePasswordPolicyChangeAfterReset","displayName":"Respect Resource Password Policy Change-After-Reset","helpMessage":"When this resource is specified in a Login Module (i.e., this resource is a pass-through authentication target) and the resource password policy is configured for change-after-reset, a user whose resource account password has been administratively reset will be required to change that password after successfully authenticating.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"filterWithOrInsteadOfAnd","displayName":"Filter with Or Instead of And","helpMessage":"Normally the the filter used to fetch change log entries is an and-based filter retrieving an interval of change entries. If this property is set, the filter will or together the required change numbers instead.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"principal","displayName":"Principal","helpMessage":"The distinguished name with which to authenticate to the LDAP server.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["uid=admin,ou=system"]},{"schema":{"name":"changeLogBlockSize","displayName":"Change Log Block Size","helpMessage":"The number of change log entries to fetch per query.","type":"int","required":true,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[100]},{"schema":{"name":"baseContexts","displayName":"Base Contexts","helpMessage":"One or more starting points in the LDAP tree that will be used when searching the tree. Searches are performed when discovering users from the LDAP server or when looking for the groups of which a user is a member.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["ou=people,o=isp","ou=groups,o=isp"]},{"schema":{"name":"passwordAttribute","displayName":"Password Attribute","helpMessage":"The name of the LDAP attribute which holds the password. When changing an user password, the new password is set to this attribute.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["userpassword"]},{"schema":{"name":"changeNumberAttribute","displayName":"Change Number Attribute","helpMessage":"The name of the change number attribute in the change log entry.","type":"java.lang.String","required":true,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["changeNumber"]},{"schema":{"name":"objectClassesToSynchronize","displayName":"Object Classes to Synchronize","helpMessage":"The object classes to synchronize. The change log is for all objects; this filters updates to just the listed object classes. You should not list the superclasses of an object class unless you intend to synchronize objects with any of the superclass values. For example, if only \"inetOrgPerson\" objects should be synchronized, but the superclasses of \"inetOrgPerson\" (\"person\", \"organizationalperson\" and \"top\") should be filtered out, then list only \"inetOrgPerson\" here. All objects in LDAP are subclassed from \"top\". For this reason, you should never list \"top\", otherwise no object would be filtered.","type":"[Ljava.lang.String;","required":true,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["inetOrgPerson","groupOfUniqueNames"]},{"schema":{"name":"credentials","displayName":"Password","helpMessage":"Password for the principal.","type":"org.identityconnectors.common.security.GuardedString","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["secret"]},{"schema":{"name":"attributesToSynchronize","displayName":"Attributes to Synchronize","helpMessage":"The names of the attributes to synchronize. This ignores updates from the change log if they do not update any of the named attributes. For example, if only \"department\" is listed, then only changes that affect \"department\" will be processed. All other updates are ignored. If blank (the default), then all changes are processed.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"maintainPosixGroupMembership","displayName":"Maintain POSIX Group Membership","helpMessage":"When enabled and a user is renamed or deleted, update any POSIX groups to which the user belongs to reflect the new name. Otherwise, the LDAP resource must maintain referential integrity with respect to group membership.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["truemaintainLdapGroupMembership"]}]'
                 capabilities='["CREATE","UPDATE","DELETE","SEARCH"]'/>
-  
+
+  <SyncopeRole id="GROUP_OWNER" entitlements='["USER_SEARCH","USER_READ","USER_CREATE","USER_UPDATE","USER_DELETE","ANYTYPECLASS_READ","ANYTYPE_LIST","ANYTYPECLASS_LIST","RELATIONSHIPTYPE_LIST","ANYTYPE_READ","REALM_LIST","GROUP_SEARCH","GROUP_READ","GROUP_UPDATE","GROUP_DELETE"]'/>
 </dataset>
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/TaskDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/TaskDataBinder.java
index 383d09f..eacbb5c 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/TaskDataBinder.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/TaskDataBinder.java
@@ -32,10 +32,9 @@
 
     void updateSchedTask(SchedTask task, SchedTaskTO taskTO, TaskUtils taskUtil);
 
-    String buildRefDesc(Task task);
+    String buildRefDesc(Task<?> task);
 
-    ExecTO getExecTO(TaskExec execution);
+    ExecTO getExecTO(TaskExec<?> execution);
 
-    <T extends TaskTO> T getTaskTO(Task task, TaskUtils taskUtil, boolean details);
-
+    <T extends TaskTO> T getTaskTO(Task<?> task, TaskUtils taskUtil, boolean details);
 }
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobManager.java
index e0182a4..aac0196 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobManager.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobManager.java
@@ -30,7 +30,9 @@
 @SuppressWarnings("squid:S1214")
 public interface JobManager {
 
-    String TASK_KEY = "task";
+    String TASK_TYPE = "taskType";
+
+    String TASK_KEY = "taskKey";
 
     String REPORT_KEY = "report";
 
@@ -48,7 +50,7 @@
     void register(Report report, OffsetDateTime startAt, long interruptMaxRetries, String executor)
             throws SchedulerException;
 
-    void unregister(Task task);
+    void unregister(Task<?> task);
 
     void unregister(Report report);
 }
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobNamer.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobNamer.java
index 59095f8..498cff6 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobNamer.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobNamer.java
@@ -55,7 +55,7 @@
         return getKeyFromJobName(name, "reportJob" + SyncopeConstants.UUID_REGEX, 9);
     }
 
-    public static JobKey getJobKey(final Task task) {
+    public static JobKey getJobKey(final Task<?> task) {
         return new JobKey("taskJob" + task.getKey(), Scheduler.DEFAULT_GROUP);
     }
 
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/SchedTaskJobDelegate.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/SchedTaskJobDelegate.java
index ddb6fe4..ebd2bdf 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/SchedTaskJobDelegate.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/SchedTaskJobDelegate.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.core.provisioning.api.job;
 
+import org.apache.syncope.common.lib.types.TaskType;
 import org.quartz.JobExecutionContext;
 import org.quartz.JobExecutionException;
 
@@ -26,10 +27,15 @@
     /**
      * Executes a Quartz Job to run the given Task.
      *
+     * @param taskType Type of task to run
      * @param taskKey Task key to run
      * @param dryRun indicates if execution shall be simulated with no actual changes
      * @param context Quartz' execution context, can be used to pass parameters to the job
      * @throws JobExecutionException if anything goes wrong
      */
-    void execute(String taskKey, boolean dryRun, JobExecutionContext context) throws JobExecutionException;
+    void execute(
+            TaskType taskType,
+            String taskKey,
+            boolean dryRun,
+            JobExecutionContext context) throws JobExecutionException;
 }
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationJobDelegate.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationJobDelegate.java
index 16b2e79..98400a1 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationJobDelegate.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationJobDelegate.java
@@ -25,7 +25,7 @@
 
 public interface NotificationJobDelegate extends JobDelegate {
 
-    TaskExec executeSingle(NotificationTask task, String executor);
+    TaskExec<NotificationTask> executeSingle(NotificationTask task, String executor);
 
     void execute(String executor) throws JobExecutionException;
 }
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationManager.java
index f130536..6573066 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationManager.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationManager.java
@@ -104,6 +104,5 @@
      * @param execution task execution.
      * @return merged task execution.
      */
-    TaskExec storeExec(TaskExec execution);
-
+    TaskExec<NotificationTask> storeExec(TaskExec<NotificationTask> execution);
 }
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationActions.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationActions.java
index 1daf63f..5e7890e 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationActions.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationActions.java
@@ -22,6 +22,7 @@
 import java.util.Set;
 import org.apache.syncope.common.lib.to.OrgUnit;
 import org.apache.syncope.common.lib.to.Provision;
+import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 import org.identityconnectors.framework.common.objects.ConnectorObject;
 
@@ -67,7 +68,7 @@
      * @param execution execution result
      * @param error propagation error
      */
-    default void onError(PropagationTaskInfo taskInfo, TaskExec execution, Exception error) {
+    default void onError(PropagationTaskInfo taskInfo, TaskExec<PropagationTask> execution, Exception error) {
         // do nothing
     }
 
@@ -78,7 +79,7 @@
      * @param execution execution result
      * @param afterObj connector object read after propagation
      */
-    default void after(PropagationTaskInfo taskInfo, TaskExec execution, ConnectorObject afterObj) {
+    default void after(PropagationTaskInfo taskInfo, TaskExec<PropagationTask> execution, ConnectorObject afterObj) {
         // do nothing
     }
 }
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskCallable.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskCallable.java
index 44c99d6..c7a1877 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskCallable.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskCallable.java
@@ -19,9 +19,10 @@
 package org.apache.syncope.core.provisioning.api.propagation;
 
 import java.util.concurrent.Callable;
+import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 
-public interface PropagationTaskCallable extends Callable<TaskExec> {
+public interface PropagationTaskCallable extends Callable<TaskExec<PropagationTask>> {
 
     void setTaskInfo(PropagationTaskInfo taskInfo);
 
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskExecutor.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskExecutor.java
index d8a86e7..db39fd4 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskExecutor.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskExecutor.java
@@ -20,6 +20,7 @@
 
 import java.util.Collection;
 import org.apache.syncope.common.lib.to.PropagationTaskTO;
+import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 
 /**
@@ -45,7 +46,7 @@
      * @param executor the executor of this task
      * @return the generated TaskExec
      */
-    TaskExec execute(PropagationTaskInfo taskInfo, PropagationReporter reporter, String executor);
+    TaskExec<PropagationTask> execute(PropagationTaskInfo taskInfo, PropagationReporter reporter, String executor);
 
     /**
      * Execute the given collection of tasks.
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningProfile.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningProfile.java
index a4c319f..05441b2 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningProfile.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningProfile.java
@@ -25,7 +25,7 @@
 import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask;
 import org.apache.syncope.core.provisioning.api.Connector;
 
-public class ProvisioningProfile<T extends ProvisioningTask, A extends ProvisioningActions> {
+public class ProvisioningProfile<T extends ProvisioningTask<?>, A extends ProvisioningActions> {
 
     private final Connector connector;
 
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeResultHandler.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeResultHandler.java
index 16d958f..541e8b0 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeResultHandler.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeResultHandler.java
@@ -20,10 +20,9 @@
 
 import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask;
 
-public interface SyncopeResultHandler<T extends ProvisioningTask, A extends ProvisioningActions> {
+public interface SyncopeResultHandler<T extends ProvisioningTask<?>, A extends ProvisioningActions> {
 
     ProvisioningProfile<T, A> getProfile();
 
     void setProfile(ProvisioningProfile<T, A> profile);
-
 }
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/job/JobNamerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/job/JobNamerTest.java
index 387b541..3739ab7 100644
--- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/job/JobNamerTest.java
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/job/JobNamerTest.java
@@ -58,7 +58,7 @@
     }
 
     @Test
-    public void getJobKey(final @Mock Task task) {
+    public void getJobKey(final @Mock Task<?> task) {
         String uuid = UUID.randomUUID().toString();
         when(task.getKey()).thenReturn(uuid);
         assertTrue(EqualsBuilder.reflectionEquals(new JobKey("taskJob" + task.getKey(), Scheduler.DEFAULT_GROUP),
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
index b728ec7..97ad8f3 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
@@ -267,12 +267,10 @@
                 try {
                     attr.add(validator, value, anyUtils);
                 } catch (InvalidPlainAttrValueException e) {
-                    String valueToPrint = value.length() > 40
-                            ? value.substring(0, 20) + "..."
-                            : value;
-                    LOG.warn("Invalid value for attribute " + schema.getKey() + ": " + valueToPrint, e);
+                    LOG.warn("Invalid value for attribute {}: {}",
+                            schema.getKey(), StringUtils.abbreviate(value, 20), e);
 
-                    invalidValues.getElements().add(schema.getKey() + ": " + valueToPrint + " - " + e.getMessage());
+                    invalidValues.getElements().add(schema.getKey() + ": " + value + " - " + e.getMessage());
                 }
             }
         });
@@ -292,7 +290,7 @@
             }
             if (intAttrName != null && intAttrName.getSchema() != null) {
                 AttrSchemaType schemaType = intAttrName.getSchema() instanceof PlainSchema
-                        ? ((PlainSchema) intAttrName.getSchema()).getType()
+                        ? intAttrName.getSchema().getType()
                         : AttrSchemaType.String;
 
                 Pair<AttrSchemaType, List<PlainAttrValue>> intValues = mappingManager.getIntValues(
@@ -362,10 +360,11 @@
             allowedPlainSchemas.getForMemberships().forEach((group, schemas) -> {
                 GroupableRelatable<?, ?, ?, ?, ?> groupable = GroupableRelatable.class.cast(any);
                 Membership<?> membership = groupable.getMembership(group.getKey()).orElse(null);
-                schemas
-                        .forEach(schema -> checkMandatory(schema, groupable.getPlainAttr(schema.getKey(), membership)
-                        .orElse(null),
-                        any, reqValMissing));
+                schemas.forEach(schema -> checkMandatory(
+                        schema,
+                        groupable.getPlainAttr(schema.getKey(), membership).orElse(null),
+                        any,
+                        reqValMissing));
             });
         }
 
@@ -538,7 +537,7 @@
         any.getAuxClasses().clear();
         anyCR.getAuxClasses().stream().
                 map(className -> anyTypeClassDAO.find(className)).
-                forEachOrdered(auxClass -> {
+                forEach(auxClass -> {
                     if (auxClass == null) {
                         LOG.debug("Invalid " + AnyTypeClass.class.getSimpleName() + " {}, ignoring...", auxClass);
                     } else {
@@ -607,26 +606,25 @@
 
         membershipTO.getPlainAttrs().stream().
                 filter(attrTO -> !attrTO.getValues().isEmpty()).
-                forEach(attrTO -> {
-                    PlainSchema schema = getPlainSchema(attrTO.getSchema());
-                    if (schema != null) {
-                        GroupablePlainAttr attr = (GroupablePlainAttr) GroupableRelatable.class.cast(any).
-                                getPlainAttr(schema.getKey(), membership).orElse(null);
-                        if (attr == null) {
-                            attr = anyUtils.newPlainAttr();
-                            attr.setOwner(any);
-                            attr.setMembership(membership);
-                            attr.setSchema(schema);
-                        }
-                        fillAttr(attrTO.getValues(), anyUtils, schema, attr, invalidValues);
+                forEach(attrTO -> Optional.ofNullable(getPlainSchema(attrTO.getSchema())).ifPresent(schema -> {
 
-                        if (attr.getValuesAsStrings().isEmpty()) {
-                            attr.setOwner(null);
-                        } else {
-                            any.add(attr);
-                        }
-                    }
-                });
+            GroupablePlainAttr attr = (GroupablePlainAttr) GroupableRelatable.class.cast(any).
+                    getPlainAttr(schema.getKey(), membership).
+                    orElseGet(() -> {
+                        GroupablePlainAttr gpa = anyUtils.newPlainAttr();
+                        gpa.setOwner(any);
+                        gpa.setMembership(membership);
+                        gpa.setSchema(schema);
+                        return gpa;
+                    });
+            fillAttr(attrTO.getValues(), anyUtils, schema, attr, invalidValues);
+
+            if (attr.getValuesAsStrings().isEmpty()) {
+                attr.setOwner(null);
+            } else {
+                any.add(attr);
+            }
+        }));
 
         if (!invalidValues.isEmpty()) {
             scce.addException(invalidValues);
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java
index b307673..b10f570 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java
@@ -112,7 +112,7 @@
         this.taskUtilsFactory = taskUtilsFactory;
     }
 
-    protected void fill(final ProvisioningTask provisioningTask, final ProvisioningTaskTO provisioningTaskTO) {
+    protected void fill(final ProvisioningTask<?> provisioningTask, final ProvisioningTaskTO provisioningTaskTO) {
         if (provisioningTask instanceof PushTask && provisioningTaskTO instanceof PushTaskTO) {
             PushTask pushTask = (PushTask) provisioningTask;
             PushTaskTO pushTaskTO = (PushTaskTO) provisioningTaskTO;
@@ -298,7 +298,7 @@
     }
 
     @Override
-    public String buildRefDesc(final Task task) {
+    public String buildRefDesc(final Task<?> task) {
         return taskUtilsFactory.getInstance(task).getType().name() + ' '
                 + "Task "
                 + task.getKey() + ' '
@@ -310,7 +310,7 @@
     }
 
     @Override
-    public ExecTO getExecTO(final TaskExec execution) {
+    public ExecTO getExecTO(final TaskExec<?> execution) {
         ExecTO execTO = new ExecTO();
         execTO.setKey(execution.getKey());
         execTO.setStatus(execution.getStatus());
@@ -349,7 +349,7 @@
 
         if (schedTaskTO instanceof ProvisioningTaskTO && schedTask instanceof ProvisioningTask) {
             ProvisioningTaskTO provisioningTaskTO = (ProvisioningTaskTO) schedTaskTO;
-            ProvisioningTask provisioningTask = (ProvisioningTask) schedTask;
+            ProvisioningTask<?> provisioningTask = (ProvisioningTask<?>) schedTask;
 
             provisioningTaskTO.setResource(provisioningTask.getResource().getKey());
 
@@ -364,11 +364,11 @@
     }
 
     @Override
-    public <T extends TaskTO> T getTaskTO(final Task task, final TaskUtils taskUtils, final boolean details) {
+    public <T extends TaskTO> T getTaskTO(final Task<?> task, final TaskUtils taskUtils, final boolean details) {
         T taskTO = taskUtils.newTaskTO();
         taskTO.setKey(task.getKey());
 
-        TaskExec latestExec = taskExecDAO.findLatestStarted(task);
+        TaskExec<?> latestExec = taskExecDAO.findLatestStarted(taskUtils.getType(), task);
         if (latestExec == null) {
             taskTO.setLatestExecStatus(StringUtils.EMPTY);
         } else {
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
index 15be775..a9c0c36 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
@@ -192,7 +192,18 @@
         return authUserTO;
     }
 
-    private void setPassword(final User user, final String password, final SyncopeClientCompositeException scce) {
+    protected RuntimeException aggregateException(
+            final SyncopeClientCompositeException scce,
+            final RuntimeException e,
+            final ClientExceptionType clientExceptionType) {
+
+        SyncopeClientException sce = SyncopeClientException.build(clientExceptionType);
+        sce.getElements().add(e.getMessage());
+        scce.addException(sce);
+        return scce;
+    }
+
+    protected void setPassword(final User user, final String password, final SyncopeClientCompositeException scce) {
         try {
             setCipherAlgorithm(user);
             user.setPassword(password);
@@ -201,7 +212,7 @@
         }
     }
 
-    private void setSecurityAnswer(
+    protected void setSecurityAnswer(
             final User user,
             final String securityAnswer,
             final SyncopeClientCompositeException scce) {
@@ -213,24 +224,14 @@
         }
     }
 
-    private void setCipherAlgorithm(final User user) {
+    protected void setCipherAlgorithm(final User user) {
         if (user.getCipherAlgorithm() == null) {
             user.setCipherAlgorithm(CipherAlgorithm.valueOf(confParamOps.get(AuthContextUtils.getDomain(),
                     "password.cipher.algorithm", CipherAlgorithm.AES.name(), String.class)));
         }
     }
 
-    private RuntimeException aggregateException(
-            final SyncopeClientCompositeException scce,
-            final RuntimeException e,
-            final ClientExceptionType clientExceptionType) {
-        SyncopeClientException sce = SyncopeClientException.build(clientExceptionType);
-        sce.getElements().add(e.getMessage());
-        scce.addException(sce);
-        return scce;
-    }
-
-    private void linkedAccount(
+    protected void linkedAccount(
             final User user,
             final LinkedAccountTO accountTO,
             final AnyUtils anyUtils,
@@ -426,8 +427,8 @@
 
         // linked accounts
         SyncopeClientException invalidValues = SyncopeClientException.build(ClientExceptionType.InvalidValues);
-        userCR.getLinkedAccounts().forEach(accountTO
-                -> linkedAccount(user, accountTO, anyUtilsFactory.getLinkedAccountInstance(), invalidValues));
+        userCR.getLinkedAccounts().
+                forEach(acct -> linkedAccount(user, acct, anyUtilsFactory.getLinkedAccountInstance(), invalidValues));
         if (!invalidValues.isEmpty()) {
             scce.addException(invalidValues);
         }
@@ -441,7 +442,7 @@
         }
     }
 
-    private boolean isPasswordMapped(final ExternalResource resource) {
+    protected boolean isPasswordMapped(final ExternalResource resource) {
         return resource.getProvision(anyTypeDAO.findUser().getKey()).
                 filter(provision -> provision.getMapping() != null).
                 map(provision -> provision.getMapping().getItems().stream().anyMatch(Item::isPassword)).
@@ -757,7 +758,7 @@
         return Pair.of(propByRes, propByLinkedAccount);
     }
 
-    private LinkedAccountTO getLinkedAccountTO(final LinkedAccount account, final boolean returnPasswordValue) {
+    protected LinkedAccountTO getLinkedAccountTO(final LinkedAccount account, final boolean returnPasswordValue) {
         LinkedAccountTO accountTO = new LinkedAccountTO.Builder(
                 account.getKey(), account.getResource().getKey(), account.getConnObjectKeyValue()).
                 username(account.getUsername()).
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java
index 28bd894..9128deb 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java
@@ -22,6 +22,7 @@
 import java.util.Optional;
 import java.util.concurrent.atomic.AtomicReference;
 import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.TaskType;
 import org.apache.syncope.core.persistence.api.dao.TaskDAO;
 import org.apache.syncope.core.persistence.api.dao.TaskExecDAO;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
@@ -40,17 +41,19 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
 
-public abstract class AbstractSchedTaskJobDelegate implements SchedTaskJobDelegate {
+public abstract class AbstractSchedTaskJobDelegate<T extends SchedTask> implements SchedTaskJobDelegate {
 
     protected static final Logger LOG = LoggerFactory.getLogger(SchedTaskJobDelegate.class);
 
     @Autowired
     private SecurityProperties securityProperties;
 
+    protected TaskType taskType;
+
     /**
      * The actual task to be executed.
      */
-    protected SchedTask task;
+    protected T task;
 
     /**
      * Task execution DAO.
@@ -100,12 +103,18 @@
         return interrupted;
     }
 
+    @SuppressWarnings("unchecked")
     @Transactional
     @Override
-    public void execute(final String taskKey, final boolean dryRun, final JobExecutionContext context)
+    public void execute(
+            final TaskType taskType,
+            final String taskKey,
+            final boolean dryRun,
+            final JobExecutionContext context)
             throws JobExecutionException {
 
-        task = taskDAO.find(taskKey);
+        this.taskType = taskType;
+        task = (T) taskDAO.find(taskType, taskKey);
         if (task == null) {
             throw new JobExecutionException("Task " + taskKey + " not found");
         }
@@ -117,7 +126,7 @@
 
         String executor = Optional.ofNullable(context.getMergedJobDataMap().getString(JobManager.EXECUTOR_KEY)).
                 orElse(securityProperties.getAdminUser());
-        TaskExec execution = entityFactory.newEntity(TaskExec.class);
+        TaskExec<SchedTask> execution = entityFactory.newTaskExec(taskType);
         execution.setStart(OffsetDateTime.now());
         execution.setTask(task);
         execution.setExecutor(executor);
@@ -142,7 +151,7 @@
         if (hasToBeRegistered(execution)) {
             register(execution);
         }
-        task = taskDAO.save(task);
+        task = (T) taskDAO.save(task);
 
         status.set("Done");
 
@@ -185,11 +194,11 @@
      * @param execution task execution
      * @return whether to persist or not
      */
-    protected boolean hasToBeRegistered(final TaskExec execution) {
+    protected boolean hasToBeRegistered(final TaskExec<?> execution) {
         return false;
     }
 
-    protected void register(final TaskExec execution) {
-        taskExecDAO.saveAndAdd(task.getKey(), execution);
+    protected void register(final TaskExec<?> execution) {
+        taskExecDAO.saveAndAdd(taskType, task.getKey(), execution);
     }
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/DefaultJobManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/DefaultJobManager.java
index abbae84..bc0345b 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/DefaultJobManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/DefaultJobManager.java
@@ -224,6 +224,11 @@
         }
 
         Map<String, Object> jobMap = createJobMapForExecutionContext(executor);
+        jobMap.put(JobManager.TASK_TYPE, task instanceof PullTask
+                ? TaskType.PULL
+                : task instanceof PushTask
+                        ? TaskType.PUSH
+                        : TaskType.SCHEDULED);
         jobMap.put(JobManager.TASK_KEY, task.getKey());
         jobMap.put(TaskJob.DELEGATE_IMPLEMENTATION, jobDelegate.getKey());
 
@@ -271,7 +276,7 @@
     }
 
     @Override
-    public void unregister(final Task task) {
+    public void unregister(final Task<?> task) {
         unregisterJob(JobNamer.getJobKey(task).getName());
     }
 
@@ -322,8 +327,8 @@
         AuthContextUtils.callAsAdmin(domain, () -> {
             // 1. jobs for SchedTasks
             Set<SchedTask> tasks = new HashSet<>(taskDAO.<SchedTask>findAll(TaskType.SCHEDULED));
-            tasks.addAll(taskDAO.<PullTask>findAll(TaskType.PULL));
-            tasks.addAll(taskDAO.<PushTask>findAll(TaskType.PUSH));
+            tasks.addAll(taskDAO.<SchedTask>findAll(TaskType.PULL));
+            tasks.addAll(taskDAO.<SchedTask>findAll(TaskType.PUSH));
 
             boolean loadException = false;
             for (Iterator<SchedTask> it = tasks.iterator(); it.hasNext() && !loadException;) {
@@ -401,8 +406,8 @@
         AuthContextUtils.callAsAdmin(domain, () -> {
             // 1. jobs for SchedTasks
             Set<SchedTask> tasks = new HashSet<>(taskDAO.<SchedTask>findAll(TaskType.SCHEDULED));
-            tasks.addAll(taskDAO.<PullTask>findAll(TaskType.PULL));
-            tasks.addAll(taskDAO.<PushTask>findAll(TaskType.PUSH));
+            tasks.addAll(taskDAO.<SchedTask>findAll(TaskType.PULL));
+            tasks.addAll(taskDAO.<SchedTask>findAll(TaskType.PUSH));
 
             tasks.forEach(task -> {
                 try {
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredAccessTokenCleanup.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredAccessTokenCleanup.java
index 532ff52..b3ae34b 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredAccessTokenCleanup.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredAccessTokenCleanup.java
@@ -19,11 +19,12 @@
 package org.apache.syncope.core.provisioning.java.job;
 
 import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO;
+import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
 import org.quartz.JobExecutionContext;
 import org.quartz.JobExecutionException;
 import org.springframework.beans.factory.annotation.Autowired;
 
-public class ExpiredAccessTokenCleanup extends AbstractSchedTaskJobDelegate {
+public class ExpiredAccessTokenCleanup extends AbstractSchedTaskJobDelegate<SchedTask> {
 
     @Autowired
     private AccessTokenDAO accessTokenDAO;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredBatchCleanup.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredBatchCleanup.java
index aee90ed..8506f88 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredBatchCleanup.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredBatchCleanup.java
@@ -19,11 +19,12 @@
 package org.apache.syncope.core.provisioning.java.job;
 
 import org.apache.syncope.core.persistence.api.dao.BatchDAO;
+import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
 import org.quartz.JobExecutionContext;
 import org.quartz.JobExecutionException;
 import org.springframework.beans.factory.annotation.Autowired;
 
-public class ExpiredBatchCleanup extends AbstractSchedTaskJobDelegate {
+public class ExpiredBatchCleanup extends AbstractSchedTaskJobDelegate<SchedTask> {
 
     @Autowired
     private BatchDAO batchDAO;
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 9f64b2c..5e13769 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
@@ -24,12 +24,14 @@
 import org.apache.syncope.common.lib.to.PropagationStatus;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ProvisionAction;
+import org.apache.syncope.common.lib.types.TaskType;
 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;
 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
+import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
 import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager;
@@ -39,7 +41,7 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
 
-public class GroupMemberProvisionTaskJobDelegate extends AbstractSchedTaskJobDelegate {
+public class GroupMemberProvisionTaskJobDelegate extends AbstractSchedTaskJobDelegate<SchedTask> {
 
     public static final String ACTION_JOBDETAIL_KEY = "action";
 
@@ -63,13 +65,17 @@
 
     @Transactional
     @Override
-    public void execute(final String taskKey, final boolean dryRun, final JobExecutionContext context)
+    public void execute(
+            final TaskType taskType,
+            final String taskKey,
+            final boolean dryRun,
+            final JobExecutionContext context)
             throws JobExecutionException {
 
         groupKey = context.getMergedJobDataMap().getString(GROUP_KEY_JOBDETAIL_KEY);
         action = (ProvisionAction) context.getMergedJobDataMap().get(ACTION_JOBDETAIL_KEY);
 
-        super.execute(taskKey, dryRun, context);
+        super.execute(taskType, taskKey, dryRun, context);
     }
 
     @Override
@@ -153,7 +159,7 @@
     }
 
     @Override
-    protected boolean hasToBeRegistered(final TaskExec execution) {
+    protected boolean hasToBeRegistered(final TaskExec<?> execution) {
         // always record execution result
         return true;
     }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SyncopeSpringBeanJobFactory.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SyncopeSpringBeanJobFactory.java
index 6565f27..d63901c 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SyncopeSpringBeanJobFactory.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SyncopeSpringBeanJobFactory.java
@@ -19,6 +19,7 @@
 package org.apache.syncope.core.provisioning.java.job;
 
 import java.util.Optional;
+import org.apache.syncope.common.lib.types.TaskType;
 import org.apache.syncope.core.provisioning.api.job.JobManager;
 import org.apache.syncope.core.provisioning.java.job.report.ReportJob;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
@@ -41,8 +42,11 @@
                 Optional.ofNullable(bundle.getJobDetail().getJobDataMap().getString(JobManager.REPORT_KEY)).
                         ifPresent(((ReportJob) job)::setReportKey);
             } else if (job instanceof TaskJob) {
-                Optional.ofNullable(bundle.getJobDetail().getJobDataMap().getString(JobManager.TASK_KEY)).
-                        ifPresent(((TaskJob) job)::setTaskKey);
+                TaskType taskType = (TaskType) bundle.getJobDetail().getJobDataMap().get(JobManager.TASK_TYPE);
+                String taskKey = bundle.getJobDetail().getJobDataMap().getString(JobManager.TASK_KEY);
+                if (taskType != null && taskKey != null) {
+                    ((TaskJob) job).setTaskInfo(taskType, taskKey);
+                }
             }
         }
 
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java
index f519390..07f3e78 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.core.provisioning.java.job;
 
+import org.apache.syncope.common.lib.types.TaskType;
 import org.apache.syncope.core.persistence.api.DomainHolder;
 import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
@@ -54,6 +55,8 @@
     @Autowired
     private DomainHolder domainHolder;
 
+    private TaskType taskType;
+
     /**
      * Key, set by the caller, for identifying the task to be executed.
      */
@@ -66,7 +69,8 @@
      *
      * @param taskKey to be set
      */
-    public void setTaskKey(final String taskKey) {
+    public void setTaskInfo(final TaskType taskType, final String taskKey) {
+        this.taskType = taskType;
         this.taskKey = taskKey;
     }
 
@@ -92,6 +96,7 @@
                         } else {
                             delegate = ImplementationManager.build(implementation);
                             delegate.execute(
+                                    taskType,
                                     taskKey,
                                     context.getMergedJobDataMap().getBoolean(DRY_RUN_JOBDETAIL_KEY),
                                     context);
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/DefaultNotificationJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/DefaultNotificationJobDelegate.java
index 969b4bd..baaeb15 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/DefaultNotificationJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/DefaultNotificationJobDelegate.java
@@ -93,8 +93,8 @@
 
     @Transactional
     @Override
-    public TaskExec executeSingle(final NotificationTask task, final String executor) {
-        TaskExec execution = entityFactory.newEntity(TaskExec.class);
+    public TaskExec<NotificationTask> executeSingle(final NotificationTask task, final String executor) {
+        TaskExec<NotificationTask> execution = entityFactory.newTaskExec(TaskType.NOTIFICATION);
         execution.setTask(task);
         execution.setStart(OffsetDateTime.now());
         execution.setExecutor(executor);
@@ -232,8 +232,8 @@
         }
     }
 
-    protected static boolean hasToBeRegistered(final TaskExec execution) {
-        NotificationTask task = (NotificationTask) execution.getTask();
+    protected static boolean hasToBeRegistered(final TaskExec<NotificationTask> execution) {
+        NotificationTask task = execution.getTask();
 
         // True if either failed and failures have to be registered, or if ALL
         // has to be registered.
@@ -242,7 +242,7 @@
                 || task.getTraceLevel() == TraceLevel.ALL;
     }
 
-    protected void handleRetries(final TaskExec execution) {
+    protected void handleRetries(final TaskExec<NotificationTask> execution) {
         if (notificationManager.getMaxRetries() <= 0) {
             return;
         }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManager.java
index 229f83f..610f6f2 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManager.java
@@ -40,6 +40,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.AuditLoggerName;
+import org.apache.syncope.common.lib.types.TaskType;
 import org.apache.syncope.core.persistence.api.dao.AnyMatchDAO;
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
@@ -452,8 +453,8 @@
     }
 
     @Override
-    public TaskExec storeExec(final TaskExec execution) {
-        NotificationTask task = taskDAO.find(execution.getTask().getKey());
+    public TaskExec<NotificationTask> storeExec(final TaskExec<NotificationTask> execution) {
+        NotificationTask task = taskDAO.find(TaskType.NOTIFICATION, execution.getTask().getKey());
         task.add(execution);
         task.setExecuted(true);
         taskDAO.save(task);
@@ -462,16 +463,16 @@
 
     @Override
     public void setTaskExecuted(final String taskKey, final boolean executed) {
-        NotificationTask task = taskDAO.find(taskKey);
+        NotificationTask task = taskDAO.find(TaskType.NOTIFICATION, taskKey);
         task.setExecuted(executed);
         taskDAO.save(task);
     }
 
     @Override
     public long countExecutionsWithStatus(final String taskKey, final String status) {
-        NotificationTask task = taskDAO.find(taskKey);
+        NotificationTask task = taskDAO.find(TaskType.NOTIFICATION, taskKey);
         long count = 0;
-        for (TaskExec taskExec : task.getExecs()) {
+        for (TaskExec<NotificationTask> taskExec : task.getExecs()) {
             if (status == null) {
                 if (taskExec.getStatus() == null) {
                     count++;
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 733da0b..5ae6d7d 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
@@ -17,6 +17,7 @@
  * under the License.
  */
 package org.apache.syncope.core.provisioning.java.propagation;
+
 import java.time.OffsetDateTime;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -38,6 +39,7 @@
 import org.apache.syncope.common.lib.types.AuditElements.Result;
 import org.apache.syncope.common.lib.types.ExecStatus;
 import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.common.lib.types.TaskType;
 import org.apache.syncope.common.lib.types.TraceLevel;
 import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager;
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
@@ -457,7 +459,7 @@
     }
 
     @Override
-    public TaskExec execute(
+    public TaskExec<PropagationTask> execute(
             final PropagationTaskInfo taskInfo,
             final PropagationReporter reporter,
             final String executor) {
@@ -465,7 +467,7 @@
         return retryTemplate(taskInfo.getResource()).map(rt -> rt.execute(context -> {
             LOG.debug("#{} Propagation attempt", context.getRetryCount());
 
-            TaskExec exec = doExecute(taskInfo, reporter, executor);
+            TaskExec<PropagationTask> exec = doExecute(taskInfo, reporter, executor);
             if (context.getRetryCount() < taskInfo.getResource().getPropagationPolicy().getMaxAttempts() - 1
                     && !ExecStatus.SUCCESS.name().equals(exec.getStatus())) {
 
@@ -481,7 +483,7 @@
                 orElse(true);
     }
 
-    protected TaskExec doExecute(
+    protected TaskExec<PropagationTask> doExecute(
             final PropagationTaskInfo taskInfo,
             final PropagationReporter reporter,
             final String executor) {
@@ -494,7 +496,7 @@
 
         OffsetDateTime start = OffsetDateTime.now();
 
-        TaskExec exec = entityFactory.newEntity(TaskExec.class);
+        TaskExec<PropagationTask> exec = entityFactory.newTaskExec(TaskType.PROPAGATION);
         exec.setStatus(ExecStatus.CREATED.name());
         exec.setExecutor(executor);
 
@@ -688,13 +690,13 @@
         return exec;
     }
 
-    protected TaskExec rejected(
+    protected TaskExec<PropagationTask> rejected(
             final PropagationTaskInfo taskInfo,
             final String rejectReason,
             final PropagationReporter reporter,
             final String executor) {
 
-        TaskExec execution = entityFactory.newEntity(TaskExec.class);
+        TaskExec<PropagationTask> execution = entityFactory.newTaskExec(TaskType.PROPAGATION);
         execution.setStatus(ExecStatus.NOT_ATTEMPTED.name());
         execution.setExecutor(executor);
         execution.setStart(OffsetDateTime.now());
@@ -729,7 +731,7 @@
      * @return true if execution has to be store, false otherwise
      */
     protected Optional<PropagationTask> hasToBeregistered(
-            final PropagationTaskInfo taskInfo, final TaskExec execution) {
+            final PropagationTaskInfo taskInfo, final TaskExec<PropagationTask> execution) {
 
         boolean result;
 
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationTaskCallable.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationTaskCallable.java
index 5310d7e..88a25ef 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationTaskCallable.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationTaskCallable.java
@@ -20,6 +20,7 @@
 
 import java.util.Collection;
 import java.util.stream.Collectors;
+import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskCallable;
@@ -73,11 +74,11 @@
     }
 
     @Override
-    public TaskExec call() throws Exception {
+    public TaskExec<PropagationTask> call() throws Exception {
         return AuthContextUtils.callAs(domain, executor, authorities, () -> {
             LOG.debug("Execution started for {}", taskInfo);
 
-            TaskExec execution = taskExecutor.execute(taskInfo, reporter, executor);
+            TaskExec<PropagationTask> execution = taskExecutor.execute(taskInfo, reporter, executor);
 
             LOG.debug("Execution completed for {}, {}", taskInfo, execution);
 
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 5fb86aa..36ecf13 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
@@ -38,6 +38,7 @@
 import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.Exec;
+import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory;
 import org.apache.syncope.core.provisioning.api.AuditManager;
@@ -146,7 +147,7 @@
 
             // first process priority resources sequentially and fail as soon as any propagation failure is reported
             prioritizedTasks.forEach(taskInfo -> {
-                TaskExec exec = null;
+                TaskExec<PropagationTask> exec = null;
                 ExecStatus execStatus;
                 String errorMessage = null;
                 try {
@@ -166,8 +167,9 @@
 
             // then process non-priority resources concurrently...
             if (!concurrentTasks.isEmpty()) {
-                CompletionService<TaskExec> completionService = new ExecutorCompletionService<>(taskExecutor);
-                List<Future<TaskExec>> futures = new ArrayList<>();
+                CompletionService<TaskExec<PropagationTask>> completionService =
+                        new ExecutorCompletionService<>(taskExecutor);
+                List<Future<TaskExec<PropagationTask>>> futures = new ArrayList<>();
 
                 concurrentTasks.forEach(taskInfo -> {
                     try {
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractProvisioningJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractProvisioningJobDelegate.java
index 284ff0f..0227097 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractProvisioningJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractProvisioningJobDelegate.java
@@ -45,7 +45,8 @@
 import org.quartz.JobExecutionException;
 import org.springframework.beans.factory.annotation.Autowired;
 
-public abstract class AbstractProvisioningJobDelegate<T extends ProvisioningTask> extends AbstractSchedTaskJobDelegate {
+public abstract class AbstractProvisioningJobDelegate<T extends ProvisioningTask<T>>
+        extends AbstractSchedTaskJobDelegate<T> {
 
     private static final String USER = "USER";
 
@@ -705,13 +706,11 @@
             throws JobExecutionException;
 
     @Override
-    protected boolean hasToBeRegistered(final TaskExec execution) {
-        final ProvisioningTask provTask = (ProvisioningTask) task;
-
+    protected boolean hasToBeRegistered(final TaskExec<?> execution) {
         // True if either failed and failures have to be registered, or if ALL has to be registered.
         return (TaskJob.Status.valueOf(execution.getStatus()) == TaskJob.Status.FAILURE
-                && provTask.getResource().getProvisioningTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal())
-                || provTask.getResource().getProvisioningTraceLevel().ordinal() >= TraceLevel.SUMMARY.ordinal();
+                && task.getResource().getProvisioningTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal())
+                || task.getResource().getProvisioningTraceLevel().ordinal() >= TraceLevel.SUMMARY.ordinal();
     }
 
     @SuppressWarnings("unchecked")
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java
index 5d9d05f..425d54b 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java
@@ -33,7 +33,7 @@
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 
-public abstract class AbstractRealmResultHandler<T extends ProvisioningTask, A extends ProvisioningActions>
+public abstract class AbstractRealmResultHandler<T extends ProvisioningTask<?>, A extends ProvisioningActions>
         implements SyncopeResultHandler<T, A> {
 
     protected static final Logger LOG = LoggerFactory.getLogger(SyncopeResultHandler.class);
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java
index 424e46d..78d2a96 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java
@@ -41,7 +41,7 @@
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 
-public abstract class AbstractSyncopeResultHandler<T extends ProvisioningTask, A extends ProvisioningActions>
+public abstract class AbstractSyncopeResultHandler<T extends ProvisioningTask<?>, A extends ProvisioningActions>
         implements SyncopeResultHandler<T, A> {
 
     protected static final Logger LOG = LoggerFactory.getLogger(SyncopeResultHandler.class);
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java
index 4d39209..915edfb 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java
@@ -34,7 +34,6 @@
 import org.apache.syncope.common.lib.types.MappingPurpose;
 import org.apache.syncope.common.lib.types.PullMode;
 import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
-import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
@@ -66,9 +65,6 @@
     @Autowired
     private RealmDAO realmDAO;
 
-    @Autowired
-    private PlainSchemaDAO plainSchemaDAO;
-
     private PullPolicy pullPolicy(
             final AnyType anyType,
             final ConflictResolutionAction conflictResolutionAction,
diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActionsTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActionsTest.java
index 3e4e55f..87ef0b5 100644
--- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActionsTest.java
+++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActionsTest.java
@@ -104,7 +104,7 @@
     private Map<String, Set<String>> membershipsAfter;
 
     @Mock
-    private ProvisioningTask provisioningTask;
+    private ProvisioningTask<?> provisioningTask;
 
     @Mock
     private ExternalResource externalResource;
@@ -150,7 +150,7 @@
         connConfProperties = new HashSet<>();
         connConfProperties.add(connConfProperty);
 
-        lenient().when(profile.getTask()).thenReturn(provisioningTask);
+        lenient().when(profile.getTask()).thenAnswer(ic -> provisioningTask);
         lenient().when(provisioningTask.getResource()).thenReturn(externalResource);
         lenient().when(anyTypeDAO.findUser()).thenAnswer(ic -> {
             AnyType userAnyType = mock(AnyType.class);
diff --git a/ext/elasticsearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindex.java b/ext/elasticsearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindex.java
index 4627643..40b0133 100644
--- a/ext/elasticsearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindex.java
+++ b/ext/elasticsearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindex.java
@@ -30,6 +30,7 @@
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
 import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.apache.syncope.ext.elasticsearch.client.ElasticsearchIndexManager;
@@ -41,7 +42,7 @@
 /**
  * Remove and rebuild all Elasticsearch indexes with information from existing users, groups and any objects.
  */
-public class ElasticsearchReindex extends AbstractSchedTaskJobDelegate {
+public class ElasticsearchReindex extends AbstractSchedTaskJobDelegate<SchedTask> {
 
     @Autowired
     protected ElasticsearchClient client;
@@ -166,7 +167,7 @@
     }
 
     @Override
-    protected boolean hasToBeRegistered(final TaskExec execution) {
+    protected boolean hasToBeRegistered(final TaskExec<?> execution) {
         return true;
     }
 }
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestDirectoryPanel.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestDirectoryPanel.java
index 6c28ec3..ac0492d 100644
--- a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestDirectoryPanel.java
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestDirectoryPanel.java
@@ -66,8 +66,6 @@
         super(id, pageRef, true);
         setFooterVisibility(false);
 
-        restClient = new UserRequestRestClient();
-
         initResultTable();
 
         IndicatingAjaxButton batchLink = new IndicatingAjaxButton("batchLink", resultTable.group.getForm()) {
@@ -82,7 +80,7 @@
                     UserRequestService service = batch.getService(UserRequestService.class);
                     items.forEach(item -> service.cancelRequest(item.getExecutionId(), null));
 
-                    Map<String, String> results = restClient.batch(batch);
+                    Map<String, String> results = UserRequestRestClient.batch(batch);
 
                     resultTable.batchModal.header(new ResourceModel("batch"));
                     resultTable.batchModal.changeCloseButtonLabel(getString("cancel", null, "Cancel"), target);
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestSampleJobDelegate.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestSampleJobDelegate.java
index 6f95f0c..9cffdee 100644
--- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestSampleJobDelegate.java
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestSampleJobDelegate.java
@@ -19,6 +19,7 @@
 package org.apache.syncope.fit.core.reference;
 
 import java.util.Date;
+import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
 import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 import org.apache.syncope.core.provisioning.java.job.AbstractSchedTaskJobDelegate;
 import org.quartz.JobExecutionContext;
@@ -27,7 +28,7 @@
 /**
  * Sample implementation for executing a scheduled task.
  */
-public class TestSampleJobDelegate extends AbstractSchedTaskJobDelegate {
+public class TestSampleJobDelegate extends AbstractSchedTaskJobDelegate<SchedTask> {
 
     @Override
     protected String doExecute(final boolean dryRun, final String executor, final JobExecutionContext context)
@@ -61,7 +62,7 @@
     }
 
     @Override
-    protected boolean hasToBeRegistered(final TaskExec execution) {
+    protected boolean hasToBeRegistered(final TaskExec<?> execution) {
         return true;
     }
 }
diff --git a/sra/src/test/java/org/apache/syncope/sra/filters/BodyPropertyAddingGatewayFilterFactory.java b/sra/src/test/java/org/apache/syncope/sra/filters/BodyPropertyAddingGatewayFilterFactory.java
index a42e738..9feca89 100644
--- a/sra/src/test/java/org/apache/syncope/sra/filters/BodyPropertyAddingGatewayFilterFactory.java
+++ b/sra/src/test/java/org/apache/syncope/sra/filters/BodyPropertyAddingGatewayFilterFactory.java
@@ -126,11 +126,11 @@
                         }
 
                         if (compressed) {
-                            try (ByteArrayOutputStream baos = new ByteArrayOutputStream(output.length);  
-                                 GZIPOutputStream gzipos = new GZIPOutputStream(baos)) {
+                            try (ByteArrayOutputStream baos = new ByteArrayOutputStream(output.length)) {
+                                try (GZIPOutputStream gzipos = new GZIPOutputStream(baos)) {
+                                    gzipos.write(output);
+                                }
 
-                                gzipos.write(output);
-                                gzipos.close();
                                 output = baos.toByteArray();
                             } catch (IOException e) {
                                 LOG.error("While GZIP-encoding output", e);