You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2017/10/10 06:36:54 UTC

[05/18] syncope git commit: [SYNCOPE-956] Core implementation

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ExternalResourceValidator.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ExternalResourceValidator.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ExternalResourceValidator.java
index 5bedb07..f4f6bc3 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ExternalResourceValidator.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ExternalResourceValidator.java
@@ -29,9 +29,7 @@ import org.apache.syncope.core.persistence.api.entity.resource.Item;
 import org.apache.syncope.core.persistence.api.entity.resource.Mapping;
 import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
 import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
-import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
 import org.identityconnectors.framework.common.objects.ObjectClass;
-import org.apache.syncope.core.provisioning.api.data.ItemTransformer;
 
 public class ExternalResourceValidator extends AbstractValidator<ExternalResourceCheck, ExternalResource> {
 
@@ -44,30 +42,7 @@ public class ExternalResourceValidator extends AbstractValidator<ExternalResourc
             return false;
         }
 
-        boolean isValid = true;
-
-        for (Item item : items) {
-            for (String className : item.getTransformerClassNames()) {
-                Class<?> actionsClass = null;
-                boolean isAssignable = false;
-                try {
-                    actionsClass = Class.forName(className);
-                    isAssignable = ItemTransformer.class.isAssignableFrom(actionsClass);
-                } catch (Exception e) {
-                    LOG.error("Invalid ItemTransformer specified: {}", className, e);
-                }
-
-                if (actionsClass == null || !isAssignable) {
-                    context.buildConstraintViolationWithTemplate(
-                            getTemplate(EntityViolationType.InvalidMapping,
-                                    "Invalid item trasformer class name")).
-                            addPropertyNode("itemTransformerClassName").addConstraintViolation();
-                    isValid = false;
-                }
-            }
-        }
-
-        return isValid;
+        return true;
     }
 
     private boolean isValid(final OrgUnit orgUnit, final ConstraintValidatorContext context) {
@@ -107,26 +82,6 @@ public class ExternalResourceValidator extends AbstractValidator<ExternalResourc
             return false;
         }
 
-        if (!resource.getPropagationActionsClassNames().isEmpty()) {
-            for (String className : resource.getPropagationActionsClassNames()) {
-                Class<?> actionsClass = null;
-                boolean isAssignable = false;
-                try {
-                    actionsClass = Class.forName(className);
-                    isAssignable = PropagationActions.class.isAssignableFrom(actionsClass);
-                } catch (Exception e) {
-                    LOG.error("Invalid PropagationActions specified: {}", className, e);
-                }
-
-                if (actionsClass == null || !isAssignable) {
-                    context.buildConstraintViolationWithTemplate(
-                            getTemplate(EntityViolationType.InvalidResource, "Invalid actions class name")).
-                            addPropertyNode("actionsClassName").addConstraintViolation();
-                    return false;
-                }
-            }
-        }
-
         final Set<AnyType> anyTypes = new HashSet<>();
         final Set<String> objectClasses = new HashSet<>();
         boolean validMappings = resource.getProvisions().stream().allMatch(provision -> {

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ProvisioningTaskValidator.java
----------------------------------------------------------------------
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 ef20ace..cdfb78b 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
@@ -20,14 +20,7 @@ package org.apache.syncope.core.persistence.jpa.validation.entity;
 
 import javax.validation.ConstraintValidatorContext;
 import org.apache.syncope.common.lib.types.EntityViolationType;
-import org.apache.syncope.common.lib.types.PullMode;
 import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask;
-import org.apache.syncope.core.persistence.jpa.entity.task.JPAPushTask;
-import org.apache.syncope.core.persistence.jpa.entity.task.JPAPullTask;
-import org.apache.syncope.core.provisioning.api.pushpull.PushActions;
-import org.apache.syncope.core.provisioning.api.pushpull.ReconciliationFilterBuilder;
-import org.apache.syncope.core.persistence.api.entity.task.PullTask;
-import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
 
 public class ProvisioningTaskValidator extends AbstractValidator<ProvisioningTaskCheck, ProvisioningTask> {
 
@@ -53,58 +46,6 @@ public class ProvisioningTaskValidator extends AbstractValidator<ProvisioningTas
                         getTemplate(EntityViolationType.InvalidProvisioningTask, "Resource cannot be null")).
                         addPropertyNode("resource").addConstraintViolation();
             }
-
-            if (!task.getActionsClassNames().isEmpty()) {
-                for (String className : task.getActionsClassNames()) {
-                    Class<?> actionsClass = null;
-                    boolean isAssignable = false;
-                    try {
-                        actionsClass = Class.forName(className);
-                        isAssignable = task instanceof JPAPullTask
-                                ? PullActions.class.isAssignableFrom(actionsClass)
-                                : task instanceof JPAPushTask
-                                        ? PushActions.class.isAssignableFrom(actionsClass)
-                                        : false;
-                    } catch (Exception e) {
-                        LOG.error("Invalid {} / {} specified",
-                                PushActions.class.getName(), PullActions.class.getName(), e);
-                        isValid = false;
-                    }
-
-                    if (actionsClass == null || !isAssignable) {
-                        isValid = false;
-
-                        context.disableDefaultConstraintViolation();
-                        context.buildConstraintViolationWithTemplate(
-                                getTemplate(EntityViolationType.InvalidProvisioningTask, "Invalid class name")).
-                                addPropertyNode("actionsClassName").addConstraintViolation();
-                    }
-                }
-            }
-
-            if (isValid && task instanceof PullTask
-                    && ((PullTask) task).getPullMode() == PullMode.FILTERED_RECONCILIATION) {
-
-                Class<?> filterBuilderClass = null;
-                boolean isAssignable = false;
-                try {
-                    filterBuilderClass = Class.forName(((PullTask) task).getReconciliationFilterBuilderClassName());
-                    isAssignable = ReconciliationFilterBuilder.class.isAssignableFrom(filterBuilderClass);
-                } catch (Exception e) {
-                    LOG.error("Invalid {} specified",
-                            ReconciliationFilterBuilder.class.getName(), PullActions.class.getName(), e);
-                    isValid = false;
-                }
-
-                if (filterBuilderClass == null || !isAssignable) {
-                    isValid = false;
-
-                    context.disableDefaultConstraintViolation();
-                    context.buildConstraintViolationWithTemplate(
-                            getTemplate(EntityViolationType.InvalidProvisioningTask, "Invalid class name")).
-                            addPropertyNode("reconciliationFilterBuilderClassName").addConstraintViolation();
-                }
-            }
         }
 
         return isValid;

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RealmValidator.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RealmValidator.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RealmValidator.java
index 1f27cd8..6b542d0 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RealmValidator.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RealmValidator.java
@@ -23,7 +23,6 @@ import javax.validation.ConstraintValidatorContext;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.types.EntityViolationType;
 import org.apache.syncope.core.persistence.api.entity.Realm;
-import org.apache.syncope.core.provisioning.api.LogicActions;
 
 public class RealmValidator extends AbstractValidator<RealmCheck, Realm> {
 
@@ -64,29 +63,6 @@ public class RealmValidator extends AbstractValidator<RealmCheck, Realm> {
             }
         }
 
-        if (!realm.getActionsClassNames().isEmpty()) {
-            for (String className : realm.getActionsClassNames()) {
-                Class<?> actionsClass = null;
-                boolean isAssignable = false;
-                try {
-                    actionsClass = Class.forName(className);
-                    isAssignable = LogicActions.class.isAssignableFrom(actionsClass);
-                } catch (Exception e) {
-                    LOG.error("Invalid {} specified", LogicActions.class.getName(), e);
-                    isValid = false;
-                }
-
-                if (actionsClass == null || !isAssignable) {
-                    isValid = false;
-
-                    context.disableDefaultConstraintViolation();
-                    context.buildConstraintViolationWithTemplate(
-                            getTemplate(EntityViolationType.InvalidRealm, "Invalid class name")).
-                            addPropertyNode("actionsClassName").addConstraintViolation();
-                }
-            }
-        }
-
         return isValid;
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ReportValidator.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ReportValidator.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ReportValidator.java
index b6cfd26..e58a889 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ReportValidator.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ReportValidator.java
@@ -22,8 +22,8 @@ import java.text.ParseException;
 import java.util.Set;
 import java.util.stream.Collectors;
 import javax.validation.ConstraintValidatorContext;
-import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.common.lib.types.EntityViolationType;
+import org.apache.syncope.core.persistence.api.entity.Entity;
 import org.apache.syncope.core.persistence.api.entity.Report;
 import org.quartz.CronExpression;
 
@@ -48,10 +48,10 @@ public class ReportValidator extends AbstractValidator<ReportCheck, Report> {
             }
         }
 
-        Set<String> reportletNames = object.getReportletConfs().stream().
-                map(ReportletConf::getName).collect(Collectors.toSet());
-        if (reportletNames.size() != object.getReportletConfs().size()) {
-            LOG.error("Reportlet name must be unique");
+        Set<String> reportletKeys = object.getReportlets().stream().
+                map(Entity::getKey).collect(Collectors.toSet());
+        if (reportletKeys.size() != object.getReportlets().size()) {
+            LOG.error("Reportlet key must be unique");
             isValid = false;
 
             context.disableDefaultConstraintViolation();

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SchedTaskValidator.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SchedTaskValidator.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SchedTaskValidator.java
index 5c159c2..cc813c6 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SchedTaskValidator.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SchedTaskValidator.java
@@ -21,11 +21,8 @@ package org.apache.syncope.core.persistence.jpa.validation.entity;
 import java.text.ParseException;
 
 import javax.validation.ConstraintValidatorContext;
-import org.apache.commons.lang3.ClassUtils;
 import org.apache.syncope.common.lib.types.EntityViolationType;
-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.provisioning.api.job.SchedTaskJobDelegate;
 import org.quartz.CronExpression;
 
 public class SchedTaskValidator extends AbstractValidator<SchedTaskCheck, SchedTask> {
@@ -33,27 +30,7 @@ public class SchedTaskValidator extends AbstractValidator<SchedTaskCheck, SchedT
     @Override
     public boolean isValid(final SchedTask task, final ConstraintValidatorContext context) {
         boolean isValid = true;
-
-        if (!(task instanceof ProvisioningTask)) {
-            Class<?> jobDelegateClass = null;
-            try {
-                jobDelegateClass = ClassUtils.getClass(task.getJobDelegateClassName());
-                isValid = SchedTaskJobDelegate.class.isAssignableFrom(jobDelegateClass);
-            } catch (Exception e) {
-                LOG.error("Invalid JobDelegate class specified", e);
-                isValid = false;
-            }
-            if (jobDelegateClass == null || !isValid) {
-                isValid = false;
-
-                context.disableDefaultConstraintViolation();
-                context.buildConstraintViolationWithTemplate(
-                        getTemplate(EntityViolationType.InvalidSchedTask, "Invalid job delegate class name")).
-                        addPropertyNode("jobDelegateClassName").addConstraintViolation();
-            }
-        }
-
-        if (isValid && task.getCronExpression() != null) {
+        if (task.getCronExpression() != null) {
             try {
                 new CronExpression(task.getCronExpression());
             } catch (ParseException e) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/resources/domains/MasterContent.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/resources/domains/MasterContent.xml b/core/persistence-jpa/src/main/resources/domains/MasterContent.xml
index 875647b..08e241b 100644
--- a/core/persistence-jpa/src/main/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/main/resources/domains/MasterContent.xml
@@ -178,17 +178,27 @@ under the License.
   <AnyType_AnyTypeClass anyType_id="GROUP" anyTypeClass_id="BaseGroup"/>
         
   <!-- Actual plain schemas -->
+  <Implementation id="EmailAddressValidator" type="VALIDATOR" engine="JAVA"
+                  body="org.apache.syncope.core.persistence.jpa.attrvalue.validation.EmailAddressValidator"/>
   <SyncopeSchema id="email"/>
   <PlainSchema id="email" type="String" anyTypeClass_id="BaseUser"
                mandatoryCondition="false" multivalue="0" uniqueConstraint="0" readonly="0"
-               validatorClass="org.apache.syncope.core.persistence.jpa.attrvalue.validation.EmailAddressValidator"/>
+               validator_id="EmailAddressValidator"/>
 
-  <Task DTYPE="SchedTask" id="e95555d2-1b09-42c8-b25b-f4c4ec598989" name="Identity Recertification Task"  active="0"
-        jobDelegateClassName="org.apache.syncope.core.provisioning.java.job.IdentityRecertification"/>
-  
+  <Implementation id="PullJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
+                  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"/>
+
+  <Implementation id="IdentityRecertification" type="TASKJOB_DELEGATE" engine="JAVA"
+                  body="org.apache.syncope.core.provisioning.java.job.IdentityRecertification"/>
+  <Task DTYPE="SchedTask" id="e95555d2-1b09-42c8-b25b-f4c4ec598989" name="Identity Recertification Task"  active="1"
+        jobDelegate_id="IdentityRecertification"/>
+
+  <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"
-        jobDelegateClassName="org.apache.syncope.core.provisioning.java.job.ExpiredAccessTokenCleanup"
-        cronExpression="0 0/5 * * * ?"/>
+        jobDelegate_id="ExpiredAccessTokenCleanup" cronExpression="0 0/5 * * * ?"/>
 
   <!-- Password reset notifications -->
   <MailTemplate id="requestPasswordReset"
@@ -243,7 +253,8 @@ we are happy to inform you that the password request was successfully executed f
   <ReportTemplate id="empty"/>  
 
   <Report id="c3520ad9-179f-49e7-b315-d684d216dd97" name="reconciliation" active="1" template_id="empty"/>
-  <ReportletConfInstance id="d6c2b475-4860-4eb1-8fde-618299c2a97c" report_id="c3520ad9-179f-49e7-b315-d684d216dd97" 
-                         serializedInstance='{"@class":"org.apache.syncope.common.lib.report.ReconciliationReportletConf","name":"dashboardReconciliationReportlet","userMatchingCond":null,"groupMatchingCond":null,"anyObjectMatchingCond":null,"features":["key","username","groupName"]}'/>
+  <Implementation id="ReconciliationReportletConf" type="REPORTLET" engine="JAVA"
+                  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"/>
 
 </dataset>

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyImplementationLookup.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyImplementationLookup.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyImplementationLookup.java
index 1875fb1..943a0e3 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyImplementationLookup.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyImplementationLookup.java
@@ -23,6 +23,7 @@ import java.util.Set;
 import org.apache.syncope.common.lib.policy.AccountRuleConf;
 import org.apache.syncope.common.lib.policy.PasswordRuleConf;
 import org.apache.syncope.common.lib.report.ReportletConf;
+import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.core.persistence.api.ImplementationLookup;
 import org.apache.syncope.core.persistence.api.dao.AccountRule;
 import org.apache.syncope.core.persistence.api.dao.PasswordRule;
@@ -45,7 +46,7 @@ public class DummyImplementationLookup implements ImplementationLookup {
     }
 
     @Override
-    public Set<String> getClassNames(final Type type) {
+    public Set<String> getClassNames(final ImplementationType type) {
         return Collections.emptySet();
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ImplementationTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ImplementationTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ImplementationTest.java
new file mode 100644
index 0000000..86acf12
--- /dev/null
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ImplementationTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.inner;
+
+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 java.util.List;
+import org.apache.syncope.common.lib.types.ImplementationEngine;
+import org.apache.syncope.common.lib.types.ImplementationType;
+import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
+import org.apache.syncope.core.persistence.jpa.AbstractTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+@Transactional("Master")
+public class ImplementationTest extends AbstractTest {
+
+    @Autowired
+    private ImplementationDAO implementationDAO;
+
+    @Test
+    public void findAll() {
+        List<Implementation> implementations = implementationDAO.findAll();
+        assertFalse(implementations.isEmpty());
+
+        assertEquals(15, implementations.size());
+
+        implementations = implementationDAO.find(ImplementationType.PULL_ACTIONS);
+        assertEquals(1, implementations.size());
+
+        implementations = implementationDAO.find(ImplementationType.PROPAGATION_ACTIONS);
+        assertEquals(1, implementations.size());
+
+        implementations = implementationDAO.find(ImplementationType.TASKJOB_DELEGATE);
+        assertEquals(5, implementations.size());
+
+        implementations = implementationDAO.find(ImplementationType.REPORTLET);
+        assertEquals(2, implementations.size());
+
+        implementations = implementationDAO.find(ImplementationType.ACCOUNT_RULE);
+        assertEquals(2, implementations.size());
+
+        implementations = implementationDAO.find(ImplementationType.PASSWORD_RULE);
+        assertEquals(3, implementations.size());
+
+        implementations = implementationDAO.find(ImplementationType.VALIDATOR);
+        assertEquals(1, implementations.size());
+    }
+
+    @Test
+    public void create() {
+        Implementation impl = entityFactory.newEntity(Implementation.class);
+        impl.setKey("new");
+        impl.setEngine(ImplementationEngine.GROOVY);
+        impl.setType(ImplementationType.VALIDATOR);
+        impl.setBody("");
+
+        Implementation actual = implementationDAO.save(impl);
+        assertNotNull(actual);
+        assertEquals(impl, actual);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java
index a1f8141..9be9cff 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java
@@ -31,6 +31,7 @@ import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.types.AttrSchemaType;
 import org.apache.syncope.common.lib.types.EntityViolationType;
 import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException;
+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.entity.PlainSchema;
 import org.apache.syncope.core.persistence.api.entity.group.GPlainAttr;
@@ -45,6 +46,9 @@ public class PlainSchemaTest extends AbstractTest {
     @Autowired
     private PlainSchemaDAO plainSchemaDAO;
 
+    @Autowired
+    private ImplementationDAO implementationDAO;
+
     @Test
     public void findAll() {
         List<PlainSchema> schemas = plainSchemaDAO.findAll();
@@ -72,7 +76,7 @@ public class PlainSchemaTest extends AbstractTest {
         PlainSchema schema = entityFactory.newEntity(PlainSchema.class);
         schema.setKey("secondaryEmail");
         schema.setType(AttrSchemaType.String);
-        schema.setValidatorClass("org.apache.syncope.core.validation.EmailAddressValidator");
+        schema.setValidator(implementationDAO.find("EmailAddressValidator"));
         schema.setMandatoryCondition("false");
         schema.setMultivalue(true);
 
@@ -89,7 +93,7 @@ public class PlainSchemaTest extends AbstractTest {
             PlainSchema schema = entityFactory.newEntity(PlainSchema.class);
             schema.setKey("secondaryEmail");
             schema.setType(AttrSchemaType.String);
-            schema.setValidatorClass("org.apache.syncope.core.validation.EmailAddressValidator");
+            schema.setValidator(implementationDAO.find("EmailAddressValidator"));
             schema.setMandatoryCondition("false");
             schema.setMultivalue(true);
             schema.setUniqueConstraint(true);

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java
index 3f7948d..e570aef 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java
@@ -25,13 +25,18 @@ import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.util.List;
+import java.util.UUID;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf;
 import org.apache.syncope.common.lib.policy.PullPolicySpec;
+import org.apache.syncope.common.lib.types.ImplementationEngine;
+import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
+import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
 import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.persistence.api.entity.Policy;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
@@ -49,6 +54,9 @@ public class PolicyTest extends AbstractTest {
     @Autowired
     private PolicyDAO policyDAO;
 
+    @Autowired
+    private ImplementationDAO implementationDAO;
+
     @Test
     public void findAll() {
         List<Policy> policies = policyDAO.findAll();
@@ -106,20 +114,32 @@ public class PolicyTest extends AbstractTest {
 
     @Test
     public void update() {
+        PasswordPolicy policy = policyDAO.find("ce93fcda-dc3a-4369-a7b0-a6108c261c85");
+        assertNotNull(policy);
+        assertEquals(1, policy.getRules().size());
+
         DefaultPasswordRuleConf ruleConf = new DefaultPasswordRuleConf();
         ruleConf.setMaxLength(8);
         ruleConf.setMinLength(6);
 
-        PasswordPolicy policy = policyDAO.find("ce93fcda-dc3a-4369-a7b0-a6108c261c85");
-        assertNotNull(policy);
-        assertEquals(1, policy.getRuleConfs().size());
-        policy.add(ruleConf);
+        Implementation rule = entityFactory.newEntity(Implementation.class);
+        rule.setKey("PasswordRule" + UUID.randomUUID().toString());
+        rule.setEngine(ImplementationEngine.JAVA);
+        rule.setType(ImplementationType.PASSWORD_RULE);
+        rule.setBody(POJOHelper.serialize(ruleConf));
+        rule = implementationDAO.save(rule);
+
+        policy.add(rule);
 
         policy = policyDAO.save(policy);
 
         assertNotNull(policy);
-        assertEquals(((DefaultPasswordRuleConf) policy.getRuleConfs().get(1)).getMaxLength(), 8);
-        assertEquals(((DefaultPasswordRuleConf) policy.getRuleConfs().get(1)).getMinLength(), 6);
+
+        rule = policy.getRules().get(1);
+
+        DefaultPasswordRuleConf actual = POJOHelper.deserialize(rule.getBody(), DefaultPasswordRuleConf.class);
+        assertEquals(actual.getMaxLength(), 8);
+        assertEquals(actual.getMinLength(), 6);
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ReportTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ReportTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ReportTest.java
index 2bee358..e35b0bf 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ReportTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ReportTest.java
@@ -25,11 +25,16 @@ import static org.junit.jupiter.api.Assertions.assertNull;
 import java.util.List;
 import java.util.UUID;
 import org.apache.syncope.common.lib.report.UserReportletConf;
+import org.apache.syncope.common.lib.types.ImplementationEngine;
+import org.apache.syncope.common.lib.types.ImplementationType;
+import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
 import org.apache.syncope.core.persistence.api.dao.ReportDAO;
 import org.apache.syncope.core.persistence.api.dao.ReportTemplateDAO;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.Report;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
 import org.junit.jupiter.api.Test;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -42,6 +47,9 @@ public class ReportTest extends AbstractTest {
     @Autowired
     private ReportTemplateDAO reportTemplateDAO;
 
+    @Autowired
+    private ImplementationDAO implementationDAO;
+
     @Test
     public void find() {
         Report report = reportDAO.find("0062ea9c-924d-4ecf-9961-4492a8cc6d1b");
@@ -60,13 +68,27 @@ public class ReportTest extends AbstractTest {
 
     @Test
     public void save() {
+        Implementation reportlet1 = entityFactory.newEntity(Implementation.class);
+        reportlet1.setKey("UserReportlet" + UUID.randomUUID().toString());
+        reportlet1.setEngine(ImplementationEngine.JAVA);
+        reportlet1.setType(ImplementationType.REPORTLET);
+        reportlet1.setBody(POJOHelper.serialize(new UserReportletConf("first")));
+        reportlet1 = implementationDAO.save(reportlet1);
+
+        Implementation reportlet2 = entityFactory.newEntity(Implementation.class);
+        reportlet2.setKey("UserReportlet" + UUID.randomUUID().toString());
+        reportlet2.setEngine(ImplementationEngine.JAVA);
+        reportlet2.setType(ImplementationType.REPORTLET);
+        reportlet2.setBody(POJOHelper.serialize(new UserReportletConf("second")));
+        reportlet2 = implementationDAO.save(reportlet2);
+
         int beforeCount = reportDAO.findAll().size();
 
         Report report = entityFactory.newEntity(Report.class);
         report.setName("new report");
         report.setActive(true);
-        report.add(new UserReportletConf("first"));
-        report.add(new UserReportletConf("second"));
+        report.add(reportlet1);
+        report.add(reportlet2);
         report.setTemplate(reportTemplateDAO.find("sample"));
 
         report = reportDAO.save(report);

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceTest.java
----------------------------------------------------------------------
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 46116ba..5fa8b8c 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
@@ -259,7 +259,7 @@ public class ResourceTest extends AbstractTest {
         assertNotNull(ldap.getProvision(anyTypeDAO.findGroup()).get().getMapping());
 
         // need to avoid any class not defined in this Maven module
-        ldap.getPropagationActionsClassNames().clear();
+        ldap.getPropagationActions().clear();
 
         List<? extends MappingItem> items = ldap.getProvision(anyTypeDAO.findGroup()).get().getMapping().getItems();
         assertNotNull(items);

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/TaskTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/TaskTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/TaskTest.java
index 5d14e9d..d73d25d 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
@@ -28,8 +28,11 @@ import java.util.Collections;
 import java.util.Date;
 import java.util.HashSet;
 import java.util.Set;
+import java.util.UUID;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.ImplementationEngine;
+import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.common.lib.types.MatchingRule;
 import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
 import org.apache.syncope.common.lib.types.ResourceOperation;
@@ -38,10 +41,12 @@ import org.apache.syncope.common.lib.types.TaskType;
 import org.apache.syncope.common.lib.types.UnmatchingRule;
 import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
 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.UserDAO;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 import org.apache.syncope.core.persistence.api.entity.task.PushTask;
@@ -59,164 +64,167 @@ import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
 
 @Transactional("Master")
 public class TaskTest extends AbstractTest {
-    
+
     @Autowired
     private TaskDAO taskDAO;
-    
+
     @Autowired
     private TaskExecDAO taskExecDAO;
-    
+
     @Autowired
     private ExternalResourceDAO resourceDAO;
-    
+
     @Autowired
     private UserDAO userDAO;
-    
+
+    @Autowired
+    private ImplementationDAO implementationDAO;
+
     @Test
     public void read() {
         PropagationTask task = taskDAO.find("1e697572-b896-484c-ae7f-0c8f63fcbc6c");
         assertNotNull(task);
-        
+
         assertNotNull(task.getExecs());
         assertFalse(task.getExecs().isEmpty());
         assertEquals(1, task.getExecs().size());
     }
-    
+
     @Test
     public void save() {
         ExternalResource resource = resourceDAO.find("ws-target-resource-1");
         assertNotNull(resource);
-        
+
         User user = userDAO.findByUsername("verdi");
         assertNotNull(user);
-        
+
         PropagationTask task = entityFactory.newEntity(PropagationTask.class);
         task.setResource(resource);
         task.setAnyTypeKind(AnyTypeKind.USER);
         task.setAnyType(AnyTypeKind.USER.name());
         task.setOperation(ResourceOperation.CREATE);
         task.setConnObjectKey("one@two.com");
-        
+
         Set<Attribute> attributes = new HashSet<>();
         attributes.add(AttributeBuilder.build("testAttribute", "testValue1", "testValue2"));
         attributes.add(AttributeBuilder.buildPassword("password".toCharArray()));
         task.setAttributes(attributes);
-        
+
         task = taskDAO.save(task);
         assertNotNull(task);
-        
+
         PropagationTask actual = taskDAO.find(task.getKey());
         assertEquals(task, actual);
-        
+
         taskDAO.flush();
-        
+
         resource = resourceDAO.find("ws-target-resource-1");
         assertTrue(taskDAO.findAll(
                 TaskType.PROPAGATION, resource, null, null, null, -1, -1, Collections.<OrderByClause>emptyList()).
                 contains(task));
     }
-    
+
     @Test
     public void addPropagationTaskExecution() {
         PropagationTask task = taskDAO.find("1e697572-b896-484c-ae7f-0c8f63fcbc6c");
         assertNotNull(task);
-        
+
         int executionNumber = task.getExecs().size();
-        
+
         TaskExec execution = entityFactory.newEntity(TaskExec.class);
         execution.setTask(task);
         execution.setStatus(PropagationTaskExecStatus.CREATED.name());
         execution.setStart(new Date());
         task.add(execution);
-        
+
         taskDAO.save(task);
         taskDAO.flush();
-        
+
         task = taskDAO.find("1e697572-b896-484c-ae7f-0c8f63fcbc6c");
         assertNotNull(task);
-        
+
         assertEquals(executionNumber + 1, task.getExecs().size());
     }
-    
+
     @Test
     public void addPullTaskExecution() {
         PullTask task = taskDAO.find("c41b9b71-9bfa-4f90-89f2-84787def4c5c");
         assertNotNull(task);
-        
+
         int executionNumber = task.getExecs().size();
-        
+
         TaskExec execution = entityFactory.newEntity(TaskExec.class);
         execution.setStatus("Text-free status");
         execution.setTask(task);
         execution.setStart(new Date());
         execution.setMessage("A message");
         task.add(execution);
-        
+
         taskDAO.save(task);
         taskDAO.flush();
-        
+
         task = taskDAO.find("c41b9b71-9bfa-4f90-89f2-84787def4c5c");
         assertNotNull(task);
-        
+
         assertEquals(executionNumber + 1, task.getExecs().size());
     }
-    
+
     @Test
     public void addPushTaskExecution() {
         PushTask task = taskDAO.find("af558be4-9d2f-4359-bf85-a554e6e90be1");
         assertNotNull(task);
-        
+
         int executionNumber = task.getExecs().size();
-        
+
         TaskExec execution = entityFactory.newEntity(TaskExec.class);
         execution.setStatus("Text-free status");
         execution.setTask(task);
         execution.setStart(new Date());
         execution.setMessage("A message");
         task.add(execution);
-        
+
         taskDAO.save(task);
         taskDAO.flush();
-        
+
         task = taskDAO.find("af558be4-9d2f-4359-bf85-a554e6e90be1");
         assertNotNull(task);
-        
+
         assertEquals(executionNumber + 1, task.getExecs().size());
     }
-    
+
     @Test
     public void deleteTask() {
         taskDAO.delete("1e697572-b896-484c-ae7f-0c8f63fcbc6c");
-        
+
         taskDAO.flush();
-        
+
         assertNull(taskDAO.find("1e697572-b896-484c-ae7f-0c8f63fcbc6c"));
         assertNull(taskExecDAO.find("e58ca1c7-178a-4012-8a71-8aa14eaf0655"));
     }
-    
+
     @Test
     public void deleteTaskExecution() {
         TaskExec execution = taskExecDAO.find("e58ca1c7-178a-4012-8a71-8aa14eaf0655");
         int executionNumber = execution.getTask().getExecs().size();
-        
+
         taskExecDAO.delete("e58ca1c7-178a-4012-8a71-8aa14eaf0655");
-        
+
         taskExecDAO.flush();
-        
+
         assertNull(taskExecDAO.find("e58ca1c7-178a-4012-8a71-8aa14eaf0655"));
-        
+
         PropagationTask task = taskDAO.find("1e697572-b896-484c-ae7f-0c8f63fcbc6c");
         assertEquals(task.getExecs().size(), executionNumber - 1);
     }
-    
+
     @Test
     public void savePullTask() {
         ExternalResource resource = resourceDAO.find("ws-target-resource-1");
         assertNotNull(resource);
-        
+
         AnyTemplatePullTask template = entityFactory.newEntity(AnyTemplatePullTask.class);
         template.set(new UserTO());
-        
+
         PullTask task = entityFactory.newEntity(PullTask.class);
         task.setName("savePullTask");
         task.setDescription("PullTask description");
@@ -235,7 +243,7 @@ public class TaskTest extends AbstractTest {
             exception = e;
         }
         assertNotNull(exception);
-        
+
         task.setCronExpression(null);
         // this save() fails because a PullTask requires a target resource
         exception = null;
@@ -245,61 +253,63 @@ public class TaskTest extends AbstractTest {
             exception = e;
         }
         assertNotNull(exception);
-        
+
         task.setResource(resource);
-        task.getActionsClassNames().add(getClass().getName());
 
-        // this save() fails because jobActionsClassName does not implement 
-        // the right interface
-        exception = null;
-        try {
-            taskDAO.save(task);
-        } catch (InvalidEntityException e) {
-            exception = e;
-        }
-        assertNotNull(exception);
-        
-        task.getActionsClassNames().clear();
-        task.getActionsClassNames().add(PullActions.class.getName());
+        Implementation pullActions = entityFactory.newEntity(Implementation.class);
+        pullActions.setKey("PullActions" + UUID.randomUUID().toString());
+        pullActions.setEngine(ImplementationEngine.JAVA);
+        pullActions.setType(ImplementationType.PULL_ACTIONS);
+        pullActions.setBody(PullActions.class.getName());
+        pullActions = implementationDAO.save(pullActions);
+
+        task.add(pullActions);
+
         // this save() finally works
         task = taskDAO.save(task);
         assertNotNull(task);
-        
+
         PullTask actual = taskDAO.find(task.getKey());
         assertEquals(task, actual);
     }
-    
+
     @Test
     public void issueSYNCOPE144() {
         ExternalResource resource = resourceDAO.find("ws-target-resource-1");
         assertNotNull(resource);
-        
+
+        Implementation pullActions = entityFactory.newEntity(Implementation.class);
+        pullActions.setEngine(ImplementationEngine.JAVA);
+        pullActions.setType(ImplementationType.PULL_ACTIONS);
+        pullActions.setBody(PullActions.class.getName());
+        pullActions = implementationDAO.save(pullActions);
+
         PullTask task = entityFactory.newEntity(PullTask.class);
-        
+
         task.setResource(resource);
         task.setName("issueSYNCOPE144");
         task.setDescription("issueSYNCOPE144 Description");
         task.setActive(true);
         task.setPullMode(PullMode.FULL_RECONCILIATION);
-        task.getActionsClassNames().add(PullActions.class.getName());
+        task.add(pullActions);
         task.setMatchingRule(MatchingRule.UPDATE);
         task.setUnmatchingRule(UnmatchingRule.PROVISION);
-        
+
         task = taskDAO.save(task);
         assertNotNull(task);
-        
+
         PullTask actual = taskDAO.find(task.getKey());
         assertEquals(task, actual);
         assertEquals("issueSYNCOPE144", actual.getName());
         assertEquals("issueSYNCOPE144 Description", actual.getDescription());
-        
+
         actual.setName("issueSYNCOPE144_2");
         actual.setDescription("issueSYNCOPE144 Description_2");
-        
+
         actual = taskDAO.save(actual);
         assertNotNull(actual);
         assertEquals("issueSYNCOPE144_2", actual.getName());
         assertEquals("issueSYNCOPE144 Description_2", actual.getDescription());
     }
-    
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index 90a5d9e..cde22ef 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -173,30 +173,32 @@ under the License.
   <PullPolicy id="66691e96-285f-4464-bc19-e68384ea4c85" description="a pull policy"
               specification='{"conflictResolutionAction":"IGNORE"}'/>
   <PasswordPolicy id="ce93fcda-dc3a-4369-a7b0-a6108c261c85" description="a password policy"
-                  historyLength="1" allowNullPassword="1"/> 
-  <PasswordRuleConfInstance id="2e3fb762-d37a-46c6-a2e5-662b527dead3"
-                            passwordPolicy_id="ce93fcda-dc3a-4369-a7b0-a6108c261c85"
-                            serializedInstance='{"@class":"org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf","maxLength":0,"minLength":8,"nonAlphanumericRequired":false,"alphanumericRequired":false,"digitRequired":false,"lowercaseRequired":false,"uppercaseRequired":false,"mustStartWithDigit":false,"mustntStartWithDigit":false,"mustEndWithDigit":false,"mustntEndWithDigit":false,"mustStartWithNonAlpha":false,"mustStartWithAlpha":false,"mustntStartWithNonAlpha":false,"mustntStartWithAlpha":false,"mustEndWithNonAlpha":false,"mustEndWithAlpha":false,"mustntEndWithNonAlpha":false,"mustntEndWithAlpha":false,"wordsNotPermitted":[],"schemasNotPermitted":[],"prefixesNotPermitted":["notpermitted1","notpermitted2"],"suffixesNotPermitted":[]}'/>
+                  historyLength="1" allowNullPassword="1"/>
+  <Implementation id="DefaultPasswordRuleConf1" type="PASSWORD_RULE" engine="JAVA"
+                  body='{"@class":"org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf","maxLength":0,"minLength":8,"nonAlphanumericRequired":false,"alphanumericRequired":false,"digitRequired":false,"lowercaseRequired":false,"uppercaseRequired":false,"mustStartWithDigit":false,"mustntStartWithDigit":false,"mustEndWithDigit":false,"mustntEndWithDigit":false,"mustStartWithNonAlpha":false,"mustStartWithAlpha":false,"mustntStartWithNonAlpha":false,"mustntStartWithAlpha":false,"mustEndWithNonAlpha":false,"mustEndWithAlpha":false,"mustntEndWithNonAlpha":false,"mustntEndWithAlpha":false,"wordsNotPermitted":[],"schemasNotPermitted":[],"prefixesNotPermitted":["notpermitted1","notpermitted2"],"suffixesNotPermitted":[]}'/>
+  <PasswordPolicyRule policy_id="ce93fcda-dc3a-4369-a7b0-a6108c261c85" implementation_id="DefaultPasswordRuleConf1"/>
   <PullPolicy id="880f8553-069b-4aed-9930-2cd53873f544" description="another pull policy"
               specification='{"conflictResolutionAction":"ALL","correlationRules":{"USER":"[\"username\",\"firstname\"]"}}'/>
   <PasswordPolicy id="986d1236-3ac5-4a19-810c-5ab21d79cba1"
                   description="sample password policy" historyLength="0" allowNullPassword="1"/> 
-  <PasswordRuleConfInstance id="62fe25b4-9124-4f01-9bc4-0013399da893"
-                            passwordPolicy_id="986d1236-3ac5-4a19-810c-5ab21d79cba1"
-                            serializedInstance='{"@class":"org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf","maxLength":0,"minLength":10,"nonAlphanumericRequired":false,"alphanumericRequired":false,"digitRequired":true,"lowercaseRequired":false,"uppercaseRequired":false,"mustStartWithDigit":false,"mustntStartWithDigit":false,"mustEndWithDigit":false,"mustntEndWithDigit":false,"mustStartWithNonAlpha":false,"mustStartWithAlpha":false,"mustntStartWithNonAlpha":false,"mustntStartWithAlpha":false,"mustEndWithNonAlpha":false,"mustEndWithAlpha":false,"mustntEndWithNonAlpha":false,"mustntEndWithAlpha":false,"wordsNotPermitted":[],"schemasNotPermitted":[],"prefixesNotPermitted":["notpermitted1","notpermitted2"],"suffixesNotPermitted":[]}'/>
+  <Implementation id="DefaultPasswordRuleConf2" type="PASSWORD_RULE"  engine="JAVA"
+                  body='{"@class":"org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf","maxLength":0,"minLength":10,"nonAlphanumericRequired":false,"alphanumericRequired":false,"digitRequired":true,"lowercaseRequired":false,"uppercaseRequired":false,"mustStartWithDigit":false,"mustntStartWithDigit":false,"mustEndWithDigit":false,"mustntEndWithDigit":false,"mustStartWithNonAlpha":false,"mustStartWithAlpha":false,"mustntStartWithNonAlpha":false,"mustntStartWithAlpha":false,"mustEndWithNonAlpha":false,"mustEndWithAlpha":false,"mustntEndWithNonAlpha":false,"mustntEndWithAlpha":false,"wordsNotPermitted":[],"schemasNotPermitted":[],"prefixesNotPermitted":["notpermitted1","notpermitted2"],"suffixesNotPermitted":[]}'/>
+  <PasswordPolicyRule policy_id="986d1236-3ac5-4a19-810c-5ab21d79cba1" implementation_id="DefaultPasswordRuleConf2"/>
   <AccountPolicy id="20ab5a8c-4b0c-432c-b957-f7fb9784d9f7" description="an account policy"
                  propagateSuspension="0" maxAuthenticationAttempts="0"/>
-  <AccountRuleConfInstance id="5" accountPolicy_id="20ab5a8c-4b0c-432c-b957-f7fb9784d9f7"
-                           serializedInstance='{"@class":"org.apache.syncope.common.lib.policy.DefaultAccountRuleConf","maxLength":0,"minLength":0,"pattern":null,"allUpperCase":false,"allLowerCase":false,"wordsNotPermitted":[],"schemasNotPermitted":[],"prefixesNotPermitted":[],"suffixesNotPermitted":[]}'/>
+  <Implementation id="DefaultAccountRuleConf1" type="ACCOUNT_RULE" engine="JAVA"
+                  body='{"@class":"org.apache.syncope.common.lib.policy.DefaultAccountRuleConf","maxLength":0,"minLength":0,"pattern":null,"allUpperCase":false,"allLowerCase":false,"wordsNotPermitted":[],"schemasNotPermitted":[],"prefixesNotPermitted":[],"suffixesNotPermitted":[]}'/>
+  <AccountPolicyRule policy_id="20ab5a8c-4b0c-432c-b957-f7fb9784d9f7" implementation_id="DefaultAccountRuleConf1"/>
   <AccountPolicy id="06e2ed52-6966-44aa-a177-a0ca7434201f" description="sample account policy"
                  propagateSuspension="0" maxAuthenticationAttempts="3"/>
-  <AccountRuleConfInstance id="6" accountPolicy_id="06e2ed52-6966-44aa-a177-a0ca7434201f"
-                           serializedInstance='{"@class":"org.apache.syncope.common.lib.policy.DefaultAccountRuleConf","maxLength":0,"minLength":4,"pattern":null,"allUpperCase":false,"allLowerCase":false,"wordsNotPermitted":[],"schemasNotPermitted":[],"prefixesNotPermitted":["notpermitted1","notpermitted2"],"suffixesNotPermitted":[]}'/>
+  <Implementation id="DefaultAccountRuleConf2" type="ACCOUNT_RULE"  engine="JAVA"
+                  body='{"@class":"org.apache.syncope.common.lib.policy.DefaultAccountRuleConf","maxLength":0,"minLength":4,"pattern":null,"allUpperCase":false,"allLowerCase":false,"wordsNotPermitted":[],"schemasNotPermitted":[],"prefixesNotPermitted":["notpermitted1","notpermitted2"],"suffixesNotPermitted":[]}'/>
+  <AccountPolicyRule policy_id="06e2ed52-6966-44aa-a177-a0ca7434201f" implementation_id="DefaultAccountRuleConf2"/>
   <PullPolicy id="4ad10d94-e002-4b3f-b771-16089cc71da9" description="pull policy 1" specification='{"conflictResolutionAction":"IGNORE"}'/>
   <PasswordPolicy id="55e5de0b-c79c-4e66-adda-251b6fb8579a" description="sample password policy" historyLength="0" allowNullPassword="0"/> 
-  <PasswordRuleConfInstance id="69b9d9fb-b37b-48f0-adba-5080f4768a8f"
-                            passwordPolicy_id="55e5de0b-c79c-4e66-adda-251b6fb8579a"
-                            serializedInstance='{"@class":"org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf","maxLength":0,"minLength":10,"nonAlphanumericRequired":true,"alphanumericRequired":false,"digitRequired":true,"lowercaseRequired":true,"uppercaseRequired":true,"mustStartWithDigit":true,"mustntStartWithDigit":false,"mustEndWithDigit":true,"mustntEndWithDigit":false,"mustStartWithNonAlpha":false,"mustStartWithAlpha":false,"mustntStartWithNonAlpha":false,"mustntStartWithAlpha":false,"mustEndWithNonAlpha":false,"mustEndWithAlpha":false,"mustntEndWithNonAlpha":false,"mustntEndWithAlpha":false,"wordsNotPermitted":[],"schemasNotPermitted":[],"prefixesNotPermitted":["notpermitted1","notpermitted2"],"suffixesNotPermitted":[]}'/>
+  <Implementation id="DefaultAccountRuleConf3" type="PASSWORD_RULE" engine="JAVA"
+                  body='{"@class":"org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf","maxLength":0,"minLength":10,"nonAlphanumericRequired":true,"alphanumericRequired":false,"digitRequired":true,"lowercaseRequired":true,"uppercaseRequired":true,"mustStartWithDigit":true,"mustntStartWithDigit":false,"mustEndWithDigit":true,"mustntEndWithDigit":false,"mustStartWithNonAlpha":false,"mustStartWithAlpha":false,"mustntStartWithNonAlpha":false,"mustntStartWithAlpha":false,"mustEndWithNonAlpha":false,"mustEndWithAlpha":false,"mustntEndWithNonAlpha":false,"mustntEndWithAlpha":false,"wordsNotPermitted":[],"schemasNotPermitted":[],"prefixesNotPermitted":["notpermitted1","notpermitted2"],"suffixesNotPermitted":[]}'/>
+  <PasswordPolicyRule policy_id="55e5de0b-c79c-4e66-adda-251b6fb8579a" implementation_id="DefaultAccountRuleConf3"/>
   <PullPolicy id="9454b0d7-2610-400a-be82-fc23cf553dd6" description="pull policy for java rule"
               specification='{"conflictResolutionAction":"IGNORE"}'/>
 
@@ -420,9 +422,11 @@ under the License.
   <PlainSchema id="fullname" type="String" anyTypeClass_id="minimal user"
                mandatoryCondition="true" multivalue="0" uniqueConstraint="1" readonly="0"/>
   <SyncopeSchema id="userId"/>
+  <Implementation id="EmailAddressValidator" type="VALIDATOR" engine="JAVA"
+                  body="org.apache.syncope.core.persistence.jpa.attrvalue.validation.EmailAddressValidator"/>
   <PlainSchema id="userId" type="String" anyTypeClass_id="minimal user"
                mandatoryCondition="true" multivalue="0" uniqueConstraint="1" readonly="0"
-               validatorClass="org.apache.syncope.core.persistence.jpa.attrvalue.validation.EmailAddressValidator"/>
+               validator_id="EmailAddressValidator"/>
   <SyncopeSchema id="loginDate"/>
   <PlainSchema id="loginDate" type="Date" anyTypeClass_id="other"
                mandatoryCondition="false" multivalue="1" uniqueConstraint="0" readonly="0"
@@ -439,7 +443,7 @@ under the License.
   <SyncopeSchema id="email"/>
   <PlainSchema id="email" type="String" anyTypeClass_id="minimal user"
                mandatoryCondition="false" multivalue="0" uniqueConstraint="0" readonly="0"
-               validatorClass="org.apache.syncope.core.persistence.jpa.attrvalue.validation.EmailAddressValidator"/>
+               validator_id="EmailAddressValidator"/>
   <SyncopeSchema id="activationDate"/>
   <PlainSchema id="activationDate" type="Date" anyTypeClass_id="other"
                mandatoryCondition="false" multivalue="0" uniqueConstraint="0" readonly="0"
@@ -793,8 +797,9 @@ under the License.
                     randomPwdIfNotProvided="1" enforceMandatoryCondition="1" overrideCapabilities="0"
                     propagationPriority="1"
                     createTraceLevel="ALL" deleteTraceLevel="ALL" updateTraceLevel="ALL" provisioningTraceLevel="ALL"/>
-  <ExternalResource_PropActions resource_id="resource-ldap"
-                                actionClassName="org.apache.syncope.core.provisioning.java.propagation.LDAPMembershipPropagationActions"/>
+  <Implementation id="LDAPMembershipPropagationActions" type="PROPAGATION_ACTIONS" engine="JAVA"
+                  body="org.apache.syncope.core.provisioning.java.propagation.LDAPMembershipPropagationActions"/>
+  <ExternalResourcePropagationAction resource_id="resource-ldap" implementation_id="LDAPMembershipPropagationActions"/>
   <ExternalResource id="resource-ldap-orgunit" connector_id="74141a3b-0762-4720-a4aa-fc3e374ef3ef"
                     randomPwdIfNotProvided="1" enforceMandatoryCondition="1" overrideCapabilities="0"
                     propagationPriority="1"
@@ -1152,6 +1157,10 @@ under the License.
                extAttrName="surname" intAttrName="surname" mandatoryCondition="true" password="0" PURPOSE="BOTH"
                mapping_id="e6b64584-94a2-4890-b645-8494a2089011"/>
 
+  <Implementation id="PullJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
+                  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"
         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"]}]'/>
@@ -1164,133 +1173,141 @@ under the License.
         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"]}]'/>
   <Task DTYPE="PullTask" 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"/>
+        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":[{"rightType":"GROUP","rightKey":"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;"]}]}'/>
   <AnyTemplatePullTask id="b3772d66-ec06-4133-bf38-b3273845ac5b"
                        pullTask_id="c41b9b71-9bfa-4f90-89f2-84787def4c5c" 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="SchedTask" id="e95555d2-1b09-42c8-b25b-f4c4ec597979" name="SampleJob Task"  active="1"
-        jobDelegateClassName="org.apache.syncope.fit.core.reference.TestSampleJobDelegate" cronExpression="0 0 0 1 * ?"/>
+  <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 * ?"/>
+  <Implementation id="IdentityRecertification" type="TASKJOB_DELEGATE" engine="JAVA"
+                  body="org.apache.syncope.core.provisioning.java.job.IdentityRecertification"/>
   <Task DTYPE="SchedTask" id="e95555d2-1b09-42c8-b25b-f4c4ec598989" name="Identity Recertification Task"  active="1"
-        jobDelegateClassName="org.apache.syncope.core.provisioning.java.job.IdentityRecertification"/>
+        jobDelegate_id="IdentityRecertification"/>
+  <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"
-        jobDelegateClassName="org.apache.syncope.core.provisioning.java.job.ExpiredAccessTokenCleanup"
-        cronExpression="0 0/5 * * * ?"/>
+        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"
         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.506" endDate="2015-12-17 09:42:00.506" status="SUCCESS"/>
   <Task DTYPE="PullTask" 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"/>
+        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" 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"/>
+        unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
   <Task DTYPE="PullTask" 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"/>
+        unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
   <Task DTYPE="PullTask" 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"/>
+        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"
                        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":[{"schema":"show","values":["true"]}]}'/>
-  <PullTask_actionsClassNames pullTask_id="1e419ca4-ea81-4493-a14f-28b90113686d" actionClassName="org.apache.syncope.core.provisioning.java.pushpull.LDAPMembershipPullActions"/>
+  <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" 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"/>
+        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"/>  
+        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"/>
+        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"/>
+        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"/>
+        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"/>
+        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"/>
+        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"/>
+        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"/>
+        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"/>
+        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"/>
+        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"/>
+        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" 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"/>
+        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" 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"/>
+        unmatchingRule="IGNORE" matchingRule="UNLINK" active="1" jobDelegate_id="PullJobDelegate"/>
   <Task DTYPE="PullTask" 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"/>
+        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"
         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" 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"/>
+        unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/>
 
   <MailTemplate id="requestPasswordReset"
                 textTemplate="Hi,
@@ -2510,14 +2527,16 @@ $$ }&#10;
 &lt;/xsl:stylesheet&gt;&#10;"/>
   
   <Report id="0062ea9c-924d-4ecf-9961-4492a8cc6d1b" name="test" active="1" template_id="sample"/>
-  <ReportletConfInstance id="5874f3fa-578c-42c6-83b0-c11adafc98b2" report_id="0062ea9c-924d-4ecf-9961-4492a8cc6d1b" 
-                         serializedInstance='{"@class":"org.apache.syncope.common.lib.report.UserReportletConf","name":"testUserReportlet","matchingCond":null,"plainAttrs":["fullname","gender"],"derAttrs":["cn"],"virAttrs":["virtualdata"],"features":["key","username","workflowId","status","creationDate","lastLoginDate","changePwdDate","passwordHistorySize","failedLoginCount","memberships","resources"]}'/>
+  <Implementation id="UserReportletConf" type="REPORTLET" engine="JAVA"
+                  body='{"@class":"org.apache.syncope.common.lib.report.UserReportletConf","name":"testUserReportlet","matchingCond":null,"plainAttrs":["fullname","gender"],"derAttrs":["cn"],"virAttrs":["virtualdata"],"features":["key","username","workflowId","status","creationDate","lastLoginDate","changePwdDate","passwordHistorySize","failedLoginCount","memberships","resources"]}'/>
+  <ReportReportlet report_id="0062ea9c-924d-4ecf-9961-4492a8cc6d1b" implementation_id="UserReportletConf"/>
   <ReportExec report_id="0062ea9c-924d-4ecf-9961-4492a8cc6d1b" id="c13f39c5-0d35-4bff-ba79-3cd5de940369"
               status="SUCCESS" startDate="2012-02-26 15:40:04" endDate="2012-02-26 15:41:04"/>
 
   <Report id="c3520ad9-179f-49e7-b315-d684d216dd97" name="reconciliation" active="1" template_id="empty"/>
-  <ReportletConfInstance id="d6c2b475-4860-4eb1-8fde-618299c2a97c" report_id="c3520ad9-179f-49e7-b315-d684d216dd97" 
-                         serializedInstance='{"@class":"org.apache.syncope.common.lib.report.ReconciliationReportletConf","name":"dashboardReconciliationReportlet","userMatchingCond":null,"groupMatchingCond":null,"anyObjectMatchingCond":null,"features":["key","username","groupName"]}'/>
+  <Implementation id="ReconciliationReportletConf" type="REPORTLET" engine="JAVA"
+                  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"/>
   
   <SyncopeLogger logName="syncope.audit.[LOGIC]:[SyncopeLogic]:[]:[isSelfRegAllowed]:[SUCCESS]" logLevel="DEBUG" logType="AUDIT"/>
   

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/test/resources/domains/TwoContent.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/resources/domains/TwoContent.xml b/core/persistence-jpa/src/test/resources/domains/TwoContent.xml
index 4fd3db7..b35beb4 100644
--- a/core/persistence-jpa/src/test/resources/domains/TwoContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/TwoContent.xml
@@ -129,10 +129,28 @@ under the License.
 
   <AnyType id="GROUP" kind="GROUP"/>
         
+  <!-- Actual plain schemas -->
+  <Implementation id="EmailAddressValidator" type="VALIDATOR" engine="JAVA"
+                  body="org.apache.syncope.core.persistence.jpa.attrvalue.validation.EmailAddressValidator"/>
   <SyncopeSchema id="email"/>
   <PlainSchema id="email" type="String" anyTypeClass_id="BaseUser"
                mandatoryCondition="false" multivalue="0" uniqueConstraint="0" readonly="0"
-               validatorClass="org.apache.syncope.core.persistence.jpa.attrvalue.validation.EmailAddressValidator"/>
+               validator_id="EmailAddressValidator"/>
+
+  <Implementation id="PullJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
+                  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"/>
+
+  <Implementation id="IdentityRecertification" type="TASKJOB_DELEGATE" engine="JAVA"
+                  body="org.apache.syncope.core.provisioning.java.job.IdentityRecertification"/>
+  <Task DTYPE="SchedTask" id="e95555d2-1b09-42c8-b25b-f4c4ec598989" name="Identity Recertification Task"  active="1"
+        jobDelegate_id="IdentityRecertification"/>
+
+  <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 * * * ?"/>
   
   <!-- Password reset notifications -->
   <MailTemplate id="requestPasswordReset"