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:55 UTC

[06/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/attrvalue/validation/URLValidator.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/URLValidator.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/URLValidator.java
index 3138ca9..5646b0b 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/URLValidator.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/URLValidator.java
@@ -22,16 +22,11 @@ import java.net.MalformedURLException;
 import java.net.URL;
 import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidPlainAttrValueException;
 import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
-import org.apache.syncope.core.persistence.api.entity.PlainSchema;
 
 public class URLValidator extends AbstractValidator {
 
     private static final long serialVersionUID = 792457177290331518L;
 
-    public URLValidator(final PlainSchema schema) {
-        super(schema);
-    }
-
     @Override
     protected void doValidate(final PlainAttrValue attrValue) {
         try {

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/ContentLoaderHandler.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/ContentLoaderHandler.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/ContentLoaderHandler.java
index 713fee0..e67d5c8 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/ContentLoaderHandler.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/ContentLoaderHandler.java
@@ -41,21 +41,19 @@ public class ContentLoaderHandler extends DefaultHandler {
 
     private static final Logger LOG = LoggerFactory.getLogger(ContentLoaderHandler.class);
 
-    private final DataSource dataSource;
+    private final JdbcTemplate jdbcTemplate;
 
     private final String rootElement;
 
     private final boolean continueOnError;
 
     public ContentLoaderHandler(final DataSource dataSource, final String rootElement, final boolean continueOnError) {
-        this.dataSource = dataSource;
+        this.jdbcTemplate = new JdbcTemplate(dataSource);
         this.rootElement = rootElement;
         this.continueOnError = continueOnError;
     }
 
     private Object[] getParameters(final String tableName, final Attributes attrs) {
-        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
-
         Map<String, Integer> colTypes = jdbcTemplate.query(
                 "SELECT * FROM " + tableName + " WHERE 0=1", (final ResultSet rs) -> {
                     Map<String, Integer> colTypes1 = new HashMap<>();
@@ -185,7 +183,6 @@ public class ContentLoaderHandler extends DefaultHandler {
         }
         query.append(") VALUES (").append(values).append(')');
 
-        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
         try {
             jdbcTemplate.update(query.toString(), getParameters(qName, atts));
         } catch (DataAccessException e) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java
index 6faeeab..cf93dc5 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java
@@ -153,7 +153,7 @@ public abstract class AbstractAnySearchDAO extends AbstractDAO<Any<?>> implement
                     && cond.getType() != AttributeCond.Type.ISNULL
                     && cond.getType() != AttributeCond.Type.ISNOTNULL) {
 
-                schema.getValidator().validate(cond.getExpression(), attrValue);
+                ((JPAPlainSchema) schema).validator().validate(cond.getExpression(), attrValue);
             }
         } catch (ValidationException e) {
             LOG.error("Could not validate expression '" + cond.getExpression() + "'", e);
@@ -225,7 +225,7 @@ public abstract class AbstractAnySearchDAO extends AbstractDAO<Any<?>> implement
                 && condClone.getType() != AttributeCond.Type.ISNOTNULL) {
 
             try {
-                schema.getValidator().validate(condClone.getExpression(), attrValue);
+                ((JPAPlainSchema) schema).validator().validate(condClone.getExpression(), attrValue);
             } catch (ValidationException e) {
                 LOG.error("Could not validate expression '" + condClone.getExpression() + "'", e);
                 throw new IllegalArgumentException();

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultAccountRule.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultAccountRule.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultAccountRule.java
index c54517f..6faf055 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultAccountRule.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultAccountRule.java
@@ -35,16 +35,20 @@ public class DefaultAccountRule implements AccountRule {
 
     private DefaultAccountRuleConf conf;
 
-    @Transactional(readOnly = true)
     @Override
-    public void enforce(final AccountRuleConf conf, final User user) {
+    public void setConf(final AccountRuleConf conf) {
         if (conf instanceof DefaultAccountRuleConf) {
             this.conf = DefaultAccountRuleConf.class.cast(conf);
         } else {
             throw new IllegalArgumentException(
-                    AccountRuleConf.class.getName() + " expected, got " + conf.getClass().getName());
+                    DefaultAccountRuleConf.class.getName() + " expected, got " + conf.getClass().getName());
         }
 
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public void enforce(final User user) {
         this.conf.getSchemasNotPermitted().stream().
                 map(schema -> user.getPlainAttr(schema)).
                 filter(attr -> attr.isPresent()).

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPasswordRule.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPasswordRule.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPasswordRule.java
index e3eeacd..d0abddd 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPasswordRule.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPasswordRule.java
@@ -33,16 +33,24 @@ public class DefaultPasswordRule implements PasswordRule {
 
     private DefaultPasswordRuleConf conf;
 
-    @Transactional(readOnly = true)
     @Override
-    public void enforce(final PasswordRuleConf conf, final User user) {
+    public PasswordRuleConf getConf() {
+        return conf;
+    }
+
+    @Override
+    public void setConf(final PasswordRuleConf conf) {
         if (conf instanceof DefaultPasswordRuleConf) {
             this.conf = (DefaultPasswordRuleConf) conf;
         } else {
             throw new IllegalArgumentException(
-                    PasswordRuleConf.class.getName() + " expected, got " + conf.getClass().getName());
+                    DefaultPasswordRuleConf.class.getName() + " expected, got " + conf.getClass().getName());
         }
+    }
 
+    @Transactional(readOnly = true)
+    @Override
+    public void enforce(final User user) {
         this.conf.getSchemasNotPermitted().stream().
                 map(schema -> user.getPlainAttr(schema)).
                 filter(attr -> attr.isPresent()).

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
index 1aa14de..01c1c29 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
@@ -22,7 +22,6 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -123,15 +122,12 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
                 "SELECT e.realm, COUNT(e) FROM  " + JPAAnyObject.class.getSimpleName() + " e "
                 + "WHERE e.type=:type GROUP BY e.realm");
         query.setParameter("type", anyType);
+
         @SuppressWarnings("unchecked")
         List<Object[]> results = query.getResultList();
-
-        Map<String, Integer> countByRealm = new HashMap<>(results.size());
-        for (Object[] result : results) {
-            countByRealm.put(((Realm) result[0]).getFullPath(), ((Number) result[1]).intValue());
-        }
-
-        return Collections.unmodifiableMap(countByRealm);
+        return results.stream().collect(Collectors.toMap(
+                result -> ((Realm) result[0]).getFullPath(),
+                result -> ((Number) result[1]).intValue()));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java
index d777f70..62be797 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java
@@ -22,7 +22,6 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -161,15 +160,12 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
     public Map<String, Integer> countByRealm() {
         Query query = entityManager().createQuery(
                 "SELECT e.realm, COUNT(e) FROM  " + JPAGroup.class.getSimpleName() + " e GROUP BY e.realm");
+
         @SuppressWarnings("unchecked")
         List<Object[]> results = query.getResultList();
-
-        Map<String, Integer> countByRealm = new HashMap<>(results.size());
-        for (Object[] result : results) {
-            countByRealm.put(((Realm) result[0]).getFullPath(), ((Number) result[1]).intValue());
-        }
-
-        return Collections.unmodifiableMap(countByRealm);
+        return results.stream().collect(Collectors.toMap(
+                result -> ((Realm) result[0]).getFullPath(),
+                result -> ((Number) result[1]).intValue()));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAImplementationDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAImplementationDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAImplementationDAO.java
new file mode 100644
index 0000000..091c4b0
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAImplementationDAO.java
@@ -0,0 +1,70 @@
+/*
+ * 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.dao;
+
+import java.util.List;
+import javax.persistence.TypedQuery;
+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.entity.JPAImplementation;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+@Repository
+public class JPAImplementationDAO extends AbstractDAO<Implementation> implements ImplementationDAO {
+
+    @Transactional(readOnly = true)
+    @Override
+    public Implementation find(final String key) {
+        return entityManager().find(JPAImplementation.class, key);
+    }
+
+    @Override
+    public List<Implementation> find(final ImplementationType type) {
+        TypedQuery<Implementation> query = entityManager().createQuery(
+                "SELECT e FROM " + JPAImplementation.class.getSimpleName() + " e WHERE e.type=:type",
+                Implementation.class);
+        query.setParameter("type", type);
+        return query.getResultList();
+    }
+
+    @Override
+    public List<Implementation> findAll() {
+        TypedQuery<Implementation> query = entityManager().createQuery(
+                "SELECT e FROM " + JPAImplementation.class.getSimpleName() + " e", Implementation.class);
+        return query.getResultList();
+    }
+
+    @Override
+    public Implementation save(final Implementation implementation) {
+        return entityManager().merge(implementation);
+    }
+
+    @Override
+    public void delete(final String key) {
+        Implementation implementation = find(key);
+        if (implementation == null) {
+            return;
+        }
+
+        entityManager().remove(implementation);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAMailTemplateDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAMailTemplateDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAMailTemplateDAO.java
index 428e4e8..e419f20 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAMailTemplateDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAMailTemplateDAO.java
@@ -41,8 +41,8 @@ public class JPAMailTemplateDAO extends AbstractDAO<MailTemplate> implements Mai
     }
 
     @Override
-    public MailTemplate save(final MailTemplate notification) {
-        return entityManager().merge(notification);
+    public MailTemplate save(final MailTemplate template) {
+        return entityManager().merge(template);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
----------------------------------------------------------------------
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 5b6998e..92f7c0a 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
@@ -22,10 +22,10 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
@@ -44,7 +44,6 @@ import org.apache.syncope.core.provisioning.api.utils.policy.PasswordPolicyExcep
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.apache.syncope.core.spring.security.DelegatedAdministrationException;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
-import org.apache.syncope.core.persistence.api.ImplementationLookup;
 import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException;
 import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO;
 import org.apache.syncope.core.persistence.api.dao.AccountRule;
@@ -56,6 +55,7 @@ import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.entity.AccessToken;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 import org.apache.syncope.core.persistence.api.entity.Entity;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.Role;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
@@ -68,8 +68,8 @@ import org.apache.syncope.core.persistence.jpa.entity.JPAAnyUtilsFactory;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
 import org.apache.syncope.core.provisioning.api.event.AnyCreatedUpdatedEvent;
 import org.apache.syncope.core.provisioning.api.event.AnyDeletedEvent;
+import org.apache.syncope.core.spring.ImplementationManager;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.support.AbstractBeanDefinition;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
@@ -86,9 +86,6 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
     @Autowired
     private AccessTokenDAO accessTokenDAO;
 
-    @Autowired
-    private ImplementationLookup implementationLookup;
-
     @Resource(name = "adminUser")
     private String adminUser;
 
@@ -145,30 +142,24 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
     public Map<String, Integer> countByRealm() {
         Query query = entityManager().createQuery(
                 "SELECT e.realm, COUNT(e) FROM  " + JPAUser.class.getSimpleName() + " e GROUP BY e.realm");
+
         @SuppressWarnings("unchecked")
         List<Object[]> results = query.getResultList();
-
-        Map<String, Integer> countByRealm = new HashMap<>(results.size());
-        for (Object[] result : results) {
-            countByRealm.put(((Realm) result[0]).getFullPath(), ((Number) result[1]).intValue());
-        }
-
-        return Collections.unmodifiableMap(countByRealm);
+        return results.stream().collect(Collectors.toMap(
+                result -> ((Realm) result[0]).getFullPath(),
+                result -> ((Number) result[1]).intValue()));
     }
 
     @Override
     public Map<String, Integer> countByStatus() {
         Query query = entityManager().createQuery(
                 "SELECT e.status, COUNT(e) FROM  " + JPAUser.class.getSimpleName() + " e GROUP BY e.status");
+
         @SuppressWarnings("unchecked")
         List<Object[]> results = query.getResultList();
-
-        Map<String, Integer> countByStatus = new HashMap<>(results.size());
-        for (Object[] result : results) {
-            countByStatus.put(((String) result[0]), ((Number) result[1]).intValue());
-        }
-
-        return Collections.unmodifiableMap(countByStatus);
+        return results.stream().collect(Collectors.toMap(
+                result -> (String) result[0],
+                result -> ((Number) result[1]).intValue()));
     }
 
     @Override
@@ -304,28 +295,12 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
                     throw new PasswordPolicyException("Password mandatory");
                 }
 
-                policy.getRuleConfs().forEach(ruleConf -> {
-                    Class<? extends PasswordRule> ruleClass =
-                            implementationLookup.getPasswordRuleClass(ruleConf.getClass());
-                    if (ruleClass == null) {
-                        LOG.warn("Could not find matching password rule for {}", ruleConf.getClass());
-                    } else {
-                        // fetch (or create) rule
-                        PasswordRule rule;
-                        if (ApplicationContextProvider.getBeanFactory().containsSingleton(ruleClass.getName())) {
-                            rule = (PasswordRule) ApplicationContextProvider.getBeanFactory().
-                                    getSingleton(ruleClass.getName());
-                        } else {
-                            rule = (PasswordRule) ApplicationContextProvider.getBeanFactory().
-                                    createBean(ruleClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
-                            ApplicationContextProvider.getBeanFactory().
-                                    registerSingleton(ruleClass.getName(), rule);
-                        }
-
-                        // enforce rule
-                        rule.enforce(ruleConf, user);
+                for (Implementation impl : policy.getRules()) {
+                    Optional<PasswordRule> rule = ImplementationManager.buildPasswordRule(impl);
+                    if (rule.isPresent()) {
+                        rule.get().enforce(user);
                     }
-                });
+                }
 
                 if (user.verifyPasswordHistory(user.getClearPassword(), policy.getHistoryLength())) {
                     throw new PasswordPolicyException("Password value was used in the past: not allowed");
@@ -376,28 +351,12 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
             }
 
             for (AccountPolicy policy : getAccountPolicies(user)) {
-                policy.getRuleConfs().forEach(ruleConf -> {
-                    Class<? extends AccountRule> ruleClass =
-                            implementationLookup.getAccountRuleClass(ruleConf.getClass());
-                    if (ruleClass == null) {
-                        LOG.warn("Could not find matching account rule for {}", ruleConf.getClass());
-                    } else {
-                        // fetch (or create) rule
-                        AccountRule rule;
-                        if (ApplicationContextProvider.getBeanFactory().containsSingleton(ruleClass.getName())) {
-                            rule = (AccountRule) ApplicationContextProvider.getBeanFactory().
-                                    getSingleton(ruleClass.getName());
-                        } else {
-                            rule = (AccountRule) ApplicationContextProvider.getBeanFactory().
-                                    createBean(ruleClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
-                            ApplicationContextProvider.getBeanFactory().
-                                    registerSingleton(ruleClass.getName(), rule);
-                        }
-
-                        // enforce rule
-                        rule.enforce(ruleConf, user);
+                for (Implementation impl : policy.getRules()) {
+                    Optional<AccountRule> rule = ImplementationManager.buildAccountRule(impl);
+                    if (rule.isPresent()) {
+                        rule.get().enforce(user);
                     }
-                });
+                }
 
                 suspend |= user.getFailedLogins() != null && policy.getMaxAuthenticationAttempts() > 0
                         && user.getFailedLogins() > policy.getMaxAuthenticationAttempts() && !user.isSuspended();

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractEntity.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractEntity.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractEntity.java
index 08c367d..edc21db 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractEntity.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractEntity.java
@@ -24,7 +24,9 @@ import java.util.HashSet;
 import java.util.Set;
 import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.core.persistence.api.entity.Entity;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanUtils;
@@ -41,6 +43,12 @@ public abstract class AbstractEntity implements Entity {
         }
     }
 
+    protected void checkImplementationType(final Implementation object, final ImplementationType expected) {
+        if (object != null && object.getType() != expected) {
+            throw new ClassCastException("Expected " + expected + ", got " + object.getType());
+        }
+    }
+
     /**
      * @param property the integer representing a boolean value
      * @return the boolean value corresponding to the property param

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttr.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttr.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttr.java
index 0070fde..f67adef 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttr.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttr.java
@@ -48,7 +48,7 @@ public abstract class AbstractPlainAttr<O extends Any<?>> extends AbstractGenera
     protected JPAPlainSchema schema;
 
     @Override
-    public PlainSchema getSchema() {
+    public JPAPlainSchema getSchema() {
         return schema;
     }
 
@@ -71,7 +71,7 @@ public abstract class AbstractPlainAttr<O extends Any<?>> extends AbstractGenera
         checkNonNullSchema();
 
         attrValue.setAttr(this);
-        getSchema().getValidator().validate(value, attrValue);
+        getSchema().validator().validate(value, attrValue);
 
         if (getSchema().isUniqueConstraint()) {
             setUniqueValue((PlainAttrUniqueValue) attrValue);

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
----------------------------------------------------------------------
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 985bfa0..3223f18 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
@@ -127,6 +127,7 @@ import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
 import org.apache.syncope.core.persistence.jpa.entity.resource.JPAOrgUnit;
 import org.apache.syncope.core.persistence.api.entity.DynRealm;
 import org.apache.syncope.core.persistence.api.entity.DynRealmMembership;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResourceHistoryConf;
 import org.apache.syncope.core.persistence.api.entity.resource.OrgUnitItem;
 import org.apache.syncope.core.persistence.jpa.entity.resource.JPAExternalResourceHistoryConf;
@@ -272,6 +273,8 @@ public class JPAEntityFactory implements EntityFactory {
             result = (E) new JPAUDynGroupMembership();
         } else if (reference.equals(AccessToken.class)) {
             result = (E) new JPAAccessToken();
+        } else if (reference.equals(Implementation.class)) {
+            result = (E) new JPAImplementation();
         } else {
             throw new IllegalArgumentException("Could not find a JPA implementation of " + reference.getName());
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAImplementation.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAImplementation.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAImplementation.java
new file mode 100644
index 0000000..dfa1134
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAImplementation.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.entity;
+
+import javax.persistence.Cacheable;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.Lob;
+import javax.persistence.Table;
+import org.apache.syncope.common.lib.types.ImplementationEngine;
+import org.apache.syncope.common.lib.types.ImplementationType;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
+
+@Entity
+@Table(name = JPAImplementation.TABLE)
+@Cacheable
+public class JPAImplementation extends AbstractProvidedKeyEntity implements Implementation {
+
+    public static final String TABLE = "Implementation";
+
+    private static final long serialVersionUID = 8700713975100295322L;
+
+    @Column(nullable = false)
+    @Enumerated(EnumType.STRING)
+    private ImplementationEngine engine;
+
+    @Column(nullable = false)
+    @Enumerated(EnumType.STRING)
+    private ImplementationType type;
+
+    @Lob
+    private String body;
+
+    @Override
+    public ImplementationEngine getEngine() {
+        return engine;
+    }
+
+    @Override
+    public void setEngine(final ImplementationEngine engine) {
+        this.engine = engine;
+    }
+
+    @Override
+    public ImplementationType getType() {
+        return type;
+    }
+
+    @Override
+    public void setType(final ImplementationType type) {
+        this.type = type;
+    }
+
+    @Override
+    public String getBody() {
+        return body;
+    }
+
+    @Override
+    public void setBody(final String body) {
+        this.body = body;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPANotification.java
----------------------------------------------------------------------
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 bd9a148..b895d65 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
@@ -33,13 +33,16 @@ import javax.persistence.FetchType;
 import javax.persistence.JoinColumn;
 import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
 import javax.persistence.Table;
 import javax.validation.constraints.Max;
 import javax.validation.constraints.Min;
 import javax.validation.constraints.NotNull;
+import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.common.lib.types.TraceLevel;
 import org.apache.syncope.core.persistence.api.entity.AnyAbout;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
+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;
 
@@ -73,7 +76,8 @@ public class JPANotification extends AbstractGeneratedKeyEntity implements Notif
     @NotNull
     private String recipientAttrName;
 
-    private String recipientsProviderClassName;
+    @OneToOne
+    private JPAImplementation recipientsProvider;
 
     @NotNull
     @Basic
@@ -131,13 +135,15 @@ public class JPANotification extends AbstractGeneratedKeyEntity implements Notif
     }
 
     @Override
-    public String getRecipientsProviderClassName() {
-        return recipientsProviderClassName;
+    public Implementation getRecipientsProvider() {
+        return recipientsProvider;
     }
 
     @Override
-    public void setRecipientsProviderClassName(final String recipientsProviderClassName) {
-        this.recipientsProviderClassName = recipientsProviderClassName;
+    public void setRecipientsProvider(final Implementation recipientsProvider) {
+        checkType(recipientsProvider, JPAImplementation.class);
+        checkImplementationType(recipientsProvider, ImplementationType.RECIPIENTS_PROVIDER);
+        this.recipientsProvider = (JPAImplementation) recipientsProvider;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPlainSchema.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPlainSchema.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPlainSchema.java
index 8a1dcdd..e97017f 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPlainSchema.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPlainSchema.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity;
 
-import java.lang.reflect.Constructor;
 import javax.persistence.Basic;
 import javax.persistence.Column;
 import javax.persistence.Entity;
@@ -35,11 +34,14 @@ import javax.validation.constraints.Min;
 import javax.validation.constraints.NotNull;
 import org.apache.syncope.common.lib.types.AttrSchemaType;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
+import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.core.persistence.api.attrvalue.validation.Validator;
 import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.PlainSchema;
 import org.apache.syncope.core.persistence.jpa.attrvalue.validation.BasicValidator;
 import org.apache.syncope.core.persistence.jpa.validation.entity.PlainSchemaCheck;
+import org.apache.syncope.core.spring.ImplementationManager;
 
 @Entity
 @Table(name = JPAPlainSchema.TABLE)
@@ -100,8 +102,11 @@ public class JPAPlainSchema extends AbstractSchema implements PlainSchema {
     @Column(nullable = true)
     private String mimeType;
 
+    @OneToOne
+    private JPAImplementation validator;
+
     @Transient
-    private Validator validator;
+    private Validator validatorImpl;
 
     public JPAPlainSchema() {
         super();
@@ -174,38 +179,37 @@ public class JPAPlainSchema extends AbstractSchema implements PlainSchema {
         this.readonly = getBooleanAsInteger(readonly);
     }
 
-    @Override
-    public Validator getValidator() {
-        if (validator != null) {
-            return validator;
+    public Validator validator() {
+        if (validatorImpl != null) {
+            return validatorImpl;
         }
 
-        if (getValidatorClass() != null && getValidatorClass().length() > 0) {
+        if (getValidator() != null) {
             try {
-                Constructor<?> validatorConstructor = Class.forName(getValidatorClass()).
-                        getConstructor(new Class<?>[] { PlainSchema.class });
-                validator = (Validator) validatorConstructor.newInstance(this);
+                validatorImpl = ImplementationManager.build(getValidator());
             } catch (Exception e) {
-                LOG.error("Could not instantiate validator of type {}, reverting to {}",
-                        getValidatorClass(), BasicValidator.class.getSimpleName(), e);
+                LOG.error("While building {}", getValidator(), e);
             }
         }
 
-        if (validator == null) {
-            validator = new BasicValidator(this);
+        if (validatorImpl == null) {
+            validatorImpl = new BasicValidator();
         }
+        validatorImpl.setSchema(this);
 
-        return validator;
+        return validatorImpl;
     }
 
     @Override
-    public String getValidatorClass() {
-        return validatorClass;
+    public Implementation getValidator() {
+        return validator;
     }
 
     @Override
-    public void setValidatorClass(final String validatorClass) {
-        this.validatorClass = validatorClass;
+    public void setValidator(final Implementation validator) {
+        checkType(validator, JPAImplementation.class);
+        checkImplementationType(validator, ImplementationType.VALIDATOR);
+        this.validator = (JPAImplementation) validator;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java
index 40017fe..49bb739 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java
@@ -19,16 +19,11 @@
 package org.apache.syncope.core.persistence.jpa.entity;
 
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Optional;
-import java.util.Set;
 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;
@@ -41,8 +36,10 @@ import javax.persistence.UniqueConstraint;
 import javax.validation.constraints.Size;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.core.persistence.api.entity.AnyTemplateRealm;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.persistence.api.entity.Realm;
@@ -75,12 +72,13 @@ public class JPARealm extends AbstractGeneratedKeyEntity implements Realm {
     @ManyToOne(fetch = FetchType.EAGER)
     private JPAAccountPolicy accountPolicy;
 
-    @ElementCollection(fetch = FetchType.EAGER)
-    @Column(name = "actionClassName")
-    @CollectionTable(name = "Realm_actionsClassNames",
+    @ManyToMany(fetch = FetchType.EAGER)
+    @JoinTable(name = TABLE + "Action",
             joinColumns =
-            @JoinColumn(name = "realm_id", referencedColumnName = "id"))
-    private Set<String> actionsClassNames = new HashSet<>();
+            @JoinColumn(name = "realm_id"),
+            inverseJoinColumns =
+            @JoinColumn(name = "implementation_id"))
+    private List<JPAImplementation> actions = new ArrayList<>();
 
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "realm")
     private List<JPAAnyTemplateRealm> templates = new ArrayList<>();
@@ -143,8 +141,15 @@ public class JPARealm extends AbstractGeneratedKeyEntity implements Realm {
     }
 
     @Override
-    public Set<String> getActionsClassNames() {
-        return actionsClassNames;
+    public boolean add(final Implementation action) {
+        checkType(action, JPAImplementation.class);
+        checkImplementationType(action, ImplementationType.LOGIC_ACTIONS);
+        return actions.contains((JPAImplementation) action) || actions.add((JPAImplementation) action);
+    }
+
+    @Override
+    public List<? extends Implementation> getActions() {
+        return actions;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java
index a048ef4..29e7bf7 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java
@@ -20,20 +20,22 @@ package org.apache.syncope.core.persistence.jpa.entity;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.stream.Collectors;
 import javax.persistence.Basic;
 import javax.persistence.CascadeType;
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.FetchType;
 import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
 import javax.persistence.Table;
 import javax.validation.constraints.Max;
 import javax.validation.constraints.Min;
 import javax.validation.constraints.NotNull;
-import org.apache.syncope.common.lib.report.ReportletConf;
+import org.apache.syncope.common.lib.types.ImplementationType;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.Report;
 import org.apache.syncope.core.persistence.api.entity.ReportExec;
 import org.apache.syncope.core.persistence.api.entity.ReportTemplate;
@@ -51,8 +53,13 @@ public class JPAReport extends AbstractGeneratedKeyEntity implements Report {
     @Column(unique = true, nullable = false)
     private String name;
 
-    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "report")
-    private List<JPAReportletConfInstance> reportletConfs = new ArrayList<>();
+    @ManyToMany(fetch = FetchType.EAGER)
+    @JoinTable(name = TABLE + "Reportlet",
+            joinColumns =
+            @JoinColumn(name = "report_id"),
+            inverseJoinColumns =
+            @JoinColumn(name = "implementation_id"))
+    private List<JPAImplementation> reportlets = new ArrayList<>();
 
     private String cronExpression;
 
@@ -91,26 +98,15 @@ public class JPAReport extends AbstractGeneratedKeyEntity implements Report {
     }
 
     @Override
-    public boolean add(final ReportletConf reportletConf) {
-        if (reportletConf == null) {
-            return false;
-        }
-
-        JPAReportletConfInstance instance = new JPAReportletConfInstance();
-        instance.setReport(this);
-        instance.setInstance(reportletConf);
-
-        return reportletConfs.add(instance);
-    }
-
-    @Override
-    public void removeAllReportletConfs() {
-        reportletConfs.clear();
+    public boolean add(final Implementation reportlet) {
+        checkType(reportlet, JPAImplementation.class);
+        checkImplementationType(reportlet, ImplementationType.REPORTLET);
+        return reportlets.contains((JPAImplementation) reportlet) || reportlets.add((JPAImplementation) reportlet);
     }
 
     @Override
-    public List<ReportletConf> getReportletConfs() {
-        return reportletConfs.stream().map(input -> input.getInstance()).collect(Collectors.toList());
+    public List<? extends Implementation> getReportlets() {
+        return reportlets;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReportletConfInstance.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReportletConfInstance.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReportletConfInstance.java
deleted file mode 100644
index 1fe7c37..0000000
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReportletConfInstance.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.persistence.jpa.entity;
-
-import javax.persistence.Entity;
-import javax.persistence.Lob;
-import javax.persistence.ManyToOne;
-import javax.persistence.Table;
-import org.apache.syncope.common.lib.report.ReportletConf;
-import org.apache.syncope.core.persistence.api.entity.Report;
-import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
-
-@Entity
-@Table(name = JPAReportletConfInstance.TABLE)
-public class JPAReportletConfInstance extends AbstractGeneratedKeyEntity {
-
-    private static final long serialVersionUID = -2436055132955674610L;
-
-    public static final String TABLE = "ReportletConfInstance";
-
-    @Lob
-    private String serializedInstance;
-
-    @ManyToOne
-    private JPAReport report;
-
-    public Report getReport() {
-        return report;
-    }
-
-    public void setReport(final Report report) {
-        checkType(report, JPAReport.class);
-        this.report = (JPAReport) report;
-    }
-
-    public ReportletConf getInstance() {
-        return serializedInstance == null
-                ? null
-                : POJOHelper.deserialize(serializedInstance, ReportletConf.class);
-    }
-
-    public void setInstance(final ReportletConf instance) {
-        this.serializedInstance = instance == null
-                ? null
-                : POJOHelper.serialize(instance);
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountPolicy.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountPolicy.java
index 200716d..aebad0b 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountPolicy.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountPolicy.java
@@ -22,21 +22,20 @@ import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-import java.util.stream.Collectors;
 import javax.persistence.Basic;
-import javax.persistence.CascadeType;
 import javax.persistence.Entity;
 import javax.persistence.FetchType;
 import javax.persistence.JoinColumn;
 import javax.persistence.JoinTable;
 import javax.persistence.ManyToMany;
-import javax.persistence.OneToMany;
 import javax.persistence.Table;
 import javax.validation.constraints.Max;
 import javax.validation.constraints.Min;
-import org.apache.syncope.common.lib.policy.AccountRuleConf;
+import org.apache.syncope.common.lib.types.ImplementationType;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation;
 import org.apache.syncope.core.persistence.jpa.entity.resource.JPAExternalResource;
 
 @Entity
@@ -54,8 +53,13 @@ public class JPAAccountPolicy extends AbstractPolicy implements AccountPolicy {
 
     private int maxAuthenticationAttempts;
 
-    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "accountPolicy")
-    private List<JPAAccountRuleConfInstance> ruleConfs = new ArrayList<>();
+    @ManyToMany(fetch = FetchType.EAGER)
+    @JoinTable(name = TABLE + "Rule",
+            joinColumns =
+            @JoinColumn(name = "policy_id"),
+            inverseJoinColumns =
+            @JoinColumn(name = "implementation_id"))
+    private List<JPAImplementation> rules = new ArrayList<>();
 
     /**
      * Resources for alternative user authentication: if empty, only internal storage will be used.
@@ -88,26 +92,15 @@ public class JPAAccountPolicy extends AbstractPolicy implements AccountPolicy {
     }
 
     @Override
-    public boolean add(final AccountRuleConf accountRuleConf) {
-        if (accountRuleConf == null) {
-            return false;
-        }
-
-        JPAAccountRuleConfInstance instance = new JPAAccountRuleConfInstance();
-        instance.setAccountPolicy(this);
-        instance.setInstance(accountRuleConf);
-
-        return ruleConfs.add(instance);
-    }
-
-    @Override
-    public void removeAllRuleConfs() {
-        ruleConfs.clear();
+    public boolean add(final Implementation rule) {
+        checkType(rule, JPAImplementation.class);
+        checkImplementationType(rule, ImplementationType.ACCOUNT_RULE);
+        return rules.contains((JPAImplementation) rule) || rules.add((JPAImplementation) rule);
     }
 
     @Override
-    public List<AccountRuleConf> getRuleConfs() {
-        return ruleConfs.stream().map(input -> input.getInstance()).collect(Collectors.toList());
+    public List<? extends Implementation> getRules() {
+        return rules;
     }
 
     @Override
@@ -120,9 +113,4 @@ public class JPAAccountPolicy extends AbstractPolicy implements AccountPolicy {
     public Set<? extends ExternalResource> getResources() {
         return resources;
     }
-
-    @Override
-    public Set<String> getResourceKeys() {
-        return getResources().stream().map(resource -> resource.getKey()).collect(Collectors.toSet());
-    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountRuleConfInstance.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountRuleConfInstance.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountRuleConfInstance.java
deleted file mode 100644
index 7272c6e..0000000
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountRuleConfInstance.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.persistence.jpa.entity.policy;
-
-import javax.persistence.Entity;
-import javax.persistence.Lob;
-import javax.persistence.ManyToOne;
-import javax.persistence.Table;
-import org.apache.syncope.common.lib.policy.AccountRuleConf;
-import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
-import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
-import org.apache.syncope.core.persistence.jpa.entity.AbstractGeneratedKeyEntity;
-
-@Entity
-@Table(name = JPAAccountRuleConfInstance.TABLE)
-public class JPAAccountRuleConfInstance extends AbstractGeneratedKeyEntity {
-
-    private static final long serialVersionUID = -2436055132955674610L;
-
-    public static final String TABLE = "AccountRuleConfInstance";
-
-    @Lob
-    private String serializedInstance;
-
-    @ManyToOne
-    private JPAAccountPolicy accountPolicy;
-
-    public AccountPolicy getAccountPolicy() {
-        return accountPolicy;
-    }
-
-    public void setAccountPolicy(final AccountPolicy report) {
-        checkType(report, JPAAccountPolicy.class);
-        this.accountPolicy = (JPAAccountPolicy) report;
-    }
-
-    public AccountRuleConf getInstance() {
-        return serializedInstance == null
-                ? null
-                : POJOHelper.deserialize(serializedInstance, AccountRuleConf.class);
-    }
-
-    public void setInstance(final AccountRuleConf instance) {
-        this.serializedInstance = instance == null
-                ? null
-                : POJOHelper.serialize(instance);
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordPolicy.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordPolicy.java
index 1ea375d..fddf5ed 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordPolicy.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordPolicy.java
@@ -20,17 +20,19 @@ package org.apache.syncope.core.persistence.jpa.entity.policy;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.stream.Collectors;
 import javax.persistence.Basic;
-import javax.persistence.CascadeType;
 import javax.persistence.Entity;
 import javax.persistence.FetchType;
-import javax.persistence.OneToMany;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
 import javax.persistence.Table;
 import javax.validation.constraints.Max;
 import javax.validation.constraints.Min;
-import org.apache.syncope.common.lib.policy.PasswordRuleConf;
+import org.apache.syncope.common.lib.types.ImplementationType;
+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.jpa.entity.JPAImplementation;
 
 @Entity
 @Table(name = JPAPasswordPolicy.TABLE)
@@ -47,8 +49,13 @@ public class JPAPasswordPolicy extends AbstractPolicy implements PasswordPolicy
 
     private int historyLength;
 
-    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "passwordPolicy")
-    private List<JPAPasswordRuleConfInstance> ruleConfs = new ArrayList<>();
+    @ManyToMany(fetch = FetchType.EAGER)
+    @JoinTable(name = TABLE + "Rule",
+            joinColumns =
+            @JoinColumn(name = "policy_id"),
+            inverseJoinColumns =
+            @JoinColumn(name = "implementation_id"))
+    private List<JPAImplementation> rules = new ArrayList<>();
 
     @Override
     public boolean isAllowNullPassword() {
@@ -71,25 +78,14 @@ public class JPAPasswordPolicy extends AbstractPolicy implements PasswordPolicy
     }
 
     @Override
-    public boolean add(final PasswordRuleConf passwordRuleConf) {
-        if (passwordRuleConf == null) {
-            return false;
-        }
-
-        JPAPasswordRuleConfInstance instance = new JPAPasswordRuleConfInstance();
-        instance.setPasswordPolicy(this);
-        instance.setInstance(passwordRuleConf);
-
-        return ruleConfs.add(instance);
-    }
-
-    @Override
-    public void removeAllRuleConfs() {
-        ruleConfs.clear();
+    public boolean add(final Implementation rule) {
+        checkType(rule, JPAImplementation.class);
+        checkImplementationType(rule, ImplementationType.PASSWORD_RULE);
+        return rules.contains((JPAImplementation) rule) || rules.add((JPAImplementation) rule);
     }
 
     @Override
-    public List<PasswordRuleConf> getRuleConfs() {
-        return ruleConfs.stream().map(input -> input.getInstance()).collect(Collectors.toList());
+    public List<? extends Implementation> getRules() {
+        return rules;
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordRuleConfInstance.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordRuleConfInstance.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordRuleConfInstance.java
deleted file mode 100644
index 788496f..0000000
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordRuleConfInstance.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.persistence.jpa.entity.policy;
-
-import javax.persistence.Entity;
-import javax.persistence.Lob;
-import javax.persistence.ManyToOne;
-import javax.persistence.Table;
-import org.apache.syncope.common.lib.policy.PasswordRuleConf;
-import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
-import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
-import org.apache.syncope.core.persistence.jpa.entity.AbstractGeneratedKeyEntity;
-
-@Entity
-@Table(name = JPAPasswordRuleConfInstance.TABLE)
-public class JPAPasswordRuleConfInstance extends AbstractGeneratedKeyEntity {
-
-    private static final long serialVersionUID = -2436055132955674610L;
-
-    public static final String TABLE = "PasswordRuleConfInstance";
-
-    @Lob
-    private String serializedInstance;
-
-    @ManyToOne
-    private JPAPasswordPolicy passwordPolicy;
-
-    public PasswordPolicy getPasswordPolicy() {
-        return passwordPolicy;
-    }
-
-    public void setPasswordPolicy(final PasswordPolicy report) {
-        checkType(report, JPAPasswordPolicy.class);
-        this.passwordPolicy = (JPAPasswordPolicy) report;
-    }
-
-    public PasswordRuleConf getInstance() {
-        return serializedInstance == null
-                ? null
-                : POJOHelper.deserialize(serializedInstance, PasswordRuleConf.class);
-    }
-
-    public void setInstance(final PasswordRuleConf instance) {
-        this.serializedInstance = instance == null
-                ? null
-                : POJOHelper.serialize(instance);
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResource.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResource.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResource.java
index 8283df0..0dd78ea 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResource.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResource.java
@@ -35,7 +35,9 @@ import javax.persistence.EnumType;
 import javax.persistence.Enumerated;
 import javax.persistence.FetchType;
 import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
 import javax.persistence.Lob;
+import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
 import javax.persistence.OneToOne;
@@ -46,6 +48,7 @@ import javax.validation.constraints.NotNull;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.types.ConnConfProperty;
 import org.apache.syncope.common.lib.types.ConnectorCapability;
+import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.common.lib.types.TraceLevel;
 import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.ConnInstance;
@@ -53,6 +56,7 @@ import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.persistence.jpa.validation.entity.ExternalResourceCheck;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
+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.resource.Provision;
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccountPolicy;
@@ -62,6 +66,7 @@ import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPullPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy;
 import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
 import org.apache.syncope.core.persistence.jpa.entity.AbstractProvidedKeyEntity;
+import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation;
 import org.identityconnectors.framework.common.objects.ObjectClass;
 
 /**
@@ -156,15 +161,13 @@ public class JPAExternalResource extends AbstractProvidedKeyEntity implements Ex
             @JoinColumn(name = "resource_id", referencedColumnName = "id"))
     private Set<ConnectorCapability> capabilitiesOverride = new HashSet<>();
 
-    /**
-     * (Optional) classes for PropagationAction.
-     */
-    @ElementCollection(fetch = FetchType.EAGER)
-    @Column(name = "actionClassName")
-    @CollectionTable(name = "ExternalResource_PropActions",
+    @ManyToMany(fetch = FetchType.EAGER)
+    @JoinTable(name = TABLE + "PropagationAction",
             joinColumns =
-            @JoinColumn(name = "resource_id", referencedColumnName = "id"))
-    private List<String> propagationActionsClassNames = new ArrayList<>();
+            @JoinColumn(name = "resource_id"),
+            inverseJoinColumns =
+            @JoinColumn(name = "implementation_id"))
+    private List<JPAImplementation> propagationActions = new ArrayList<>();
 
     public JPAExternalResource() {
         super();
@@ -357,7 +360,16 @@ public class JPAExternalResource extends AbstractProvidedKeyEntity implements Ex
     }
 
     @Override
-    public List<String> getPropagationActionsClassNames() {
-        return propagationActionsClassNames;
+    public boolean add(final Implementation propagationAction) {
+        checkType(propagationAction, JPAImplementation.class);
+        checkImplementationType(propagationAction, ImplementationType.PROPAGATION_ACTIONS);
+        return propagationActions.contains((JPAImplementation) propagationAction)
+                || propagationActions.add((JPAImplementation) propagationAction);
     }
+
+    @Override
+    public List<? extends Implementation> getPropagationActions() {
+        return propagationActions;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAMappingItem.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAMappingItem.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAMappingItem.java
index 8624a70..1f55c8d 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAMappingItem.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAMappingItem.java
@@ -21,16 +21,18 @@ package org.apache.syncope.core.persistence.jpa.entity.resource;
 import java.util.ArrayList;
 import java.util.List;
 import javax.persistence.Cacheable;
-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.JoinTable;
+import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
 import javax.persistence.Table;
+import org.apache.syncope.common.lib.types.ImplementationType;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
 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.jpa.entity.JPAImplementation;
 
 @Entity
 @Table(name = JPAMappingItem.TABLE)
@@ -44,15 +46,13 @@ public class JPAMappingItem extends AbstractItem implements MappingItem {
     @ManyToOne
     private JPAMapping mapping;
 
-    /**
-     * (Optional) classes for MappingItem transformation.
-     */
-    @ElementCollection(fetch = FetchType.EAGER)
-    @Column(name = "transformerClassName")
-    @CollectionTable(name = TABLE + "_Transformer",
+    @ManyToMany(fetch = FetchType.EAGER)
+    @JoinTable(name = TABLE + "Transformer",
             joinColumns =
-            @JoinColumn(name = "mappingItem_id", referencedColumnName = "id"))
-    private List<String> transformerClassNames = new ArrayList<>();
+            @JoinColumn(name = "item_id"),
+            inverseJoinColumns =
+            @JoinColumn(name = "implementation_id"))
+    private List<JPAImplementation> transformers = new ArrayList<>();
 
     @Override
     public Mapping getMapping() {
@@ -66,7 +66,16 @@ public class JPAMappingItem extends AbstractItem implements MappingItem {
     }
 
     @Override
-    public List<String> getTransformerClassNames() {
-        return transformerClassNames;
+    public boolean add(final Implementation transformer) {
+        checkType(transformer, JPAImplementation.class);
+        checkImplementationType(transformer, ImplementationType.ITEM_TRANSFORMER);
+        return transformers.contains((JPAImplementation) transformer)
+                || transformers.add((JPAImplementation) transformer);
     }
+
+    @Override
+    public List<? extends Implementation> getTransformers() {
+        return transformers;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAOrgUnitItem.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAOrgUnitItem.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAOrgUnitItem.java
index 24f59ce..769ca9f 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAOrgUnitItem.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAOrgUnitItem.java
@@ -21,16 +21,18 @@ package org.apache.syncope.core.persistence.jpa.entity.resource;
 import java.util.ArrayList;
 import java.util.List;
 import javax.persistence.Cacheable;
-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.JoinTable;
+import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
 import javax.persistence.Table;
+import org.apache.syncope.common.lib.types.ImplementationType;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
 import org.apache.syncope.core.persistence.api.entity.resource.OrgUnitItem;
+import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation;
 
 @Entity
 @Table(name = JPAOrgUnitItem.TABLE)
@@ -44,15 +46,13 @@ public class JPAOrgUnitItem extends AbstractItem implements OrgUnitItem {
     @ManyToOne
     private JPAOrgUnit orgUnit;
 
-    /**
-     * (Optional) classes for Item transformation.
-     */
-    @ElementCollection(fetch = FetchType.EAGER)
-    @Column(name = "transformerClassName")
-    @CollectionTable(name = TABLE + "_Transformer",
+    @ManyToMany(fetch = FetchType.EAGER)
+    @JoinTable(name = TABLE + "Transformer",
             joinColumns =
-            @JoinColumn(name = "orgUnitItem_id", referencedColumnName = "id"))
-    private List<String> transformerClassNames = new ArrayList<>();
+            @JoinColumn(name = "item_id"),
+            inverseJoinColumns =
+            @JoinColumn(name = "implementation_id"))
+    private List<JPAImplementation> transformers = new ArrayList<>();
 
     @Override
     public OrgUnit getOrgUnit() {
@@ -66,7 +66,15 @@ public class JPAOrgUnitItem extends AbstractItem implements OrgUnitItem {
     }
 
     @Override
-    public List<String> getTransformerClassNames() {
-        return transformerClassNames;
+    public boolean add(final Implementation transformer) {
+        checkType(transformer, JPAImplementation.class);
+        checkImplementationType(transformer, ImplementationType.ITEM_TRANSFORMER);
+        return transformers.contains((JPAImplementation) transformer)
+                || transformers.add((JPAImplementation) transformer);
+    }
+
+    @Override
+    public List<? extends Implementation> getTransformers() {
+        return transformers;
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTask.java
----------------------------------------------------------------------
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 54f70cf..d42c3ea 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
@@ -19,29 +19,30 @@
 package org.apache.syncope.core.persistence.jpa.entity.task;
 
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Optional;
-import java.util.Set;
 import javax.persistence.CascadeType;
-import javax.persistence.CollectionTable;
-import javax.persistence.Column;
 import javax.persistence.DiscriminatorValue;
-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.JoinTable;
+import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
 import javax.validation.constraints.NotNull;
+import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.common.lib.types.PullMode;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.jpa.entity.JPARealm;
 import org.apache.syncope.core.persistence.api.entity.task.PullTask;
 import org.apache.syncope.core.persistence.api.entity.task.AnyTemplatePullTask;
+import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation;
 
 @Entity
 @DiscriminatorValue("PullTask")
@@ -53,17 +54,19 @@ public class JPAPullTask extends AbstractProvisioningTask implements PullTask {
     @NotNull
     private PullMode pullMode;
 
-    private String reconciliationFilterBuilderClassName;
+    @OneToOne
+    private JPAImplementation reconFilterBuilder;
 
     @ManyToOne(fetch = FetchType.EAGER, optional = false)
     private JPARealm destinationRealm;
 
-    @ElementCollection(fetch = FetchType.EAGER)
-    @Column(name = "actionClassName")
-    @CollectionTable(name = "PullTask_actionsClassNames",
+    @ManyToMany(fetch = FetchType.EAGER)
+    @JoinTable(name = "PullTaskAction",
             joinColumns =
-            @JoinColumn(name = "pullTask_id", referencedColumnName = "id"))
-    private Set<String> actionsClassNames = new HashSet<>();
+            @JoinColumn(name = "task_id"),
+            inverseJoinColumns =
+            @JoinColumn(name = "implementation_id"))
+    private List<JPAImplementation> actions = new ArrayList<>();
 
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "pullTask")
     private List<JPAAnyTemplatePullTask> templates = new ArrayList<>();
@@ -79,13 +82,15 @@ public class JPAPullTask extends AbstractProvisioningTask implements PullTask {
     }
 
     @Override
-    public String getReconciliationFilterBuilderClassName() {
-        return reconciliationFilterBuilderClassName;
+    public Implementation getReconFilterBuilder() {
+        return reconFilterBuilder;
     }
 
     @Override
-    public void setReconciliationFilterBuilderClassName(final String reconciliationFilterBuilderClassName) {
-        this.reconciliationFilterBuilderClassName = reconciliationFilterBuilderClassName;
+    public void setReconFilterBuilder(final Implementation reconFilterBuilder) {
+        checkType(reconFilterBuilder, JPAImplementation.class);
+        checkImplementationType(reconFilterBuilder, ImplementationType.RECON_FILTER_BUILDER);
+        this.reconFilterBuilder = (JPAImplementation) reconFilterBuilder;
     }
 
     @Override
@@ -100,8 +105,15 @@ public class JPAPullTask extends AbstractProvisioningTask implements PullTask {
     }
 
     @Override
-    public Set<String> getActionsClassNames() {
-        return actionsClassNames;
+    public boolean add(final Implementation action) {
+        checkType(action, JPAImplementation.class);
+        checkImplementationType(action, ImplementationType.PULL_ACTIONS);
+        return actions.contains((JPAImplementation) action) || actions.add((JPAImplementation) action);
+    }
+
+    @Override
+    public List<? extends Implementation> getActions() {
+        return actions;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPushTask.java
----------------------------------------------------------------------
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 efa84b7..abadd8c 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
@@ -19,24 +19,24 @@
 package org.apache.syncope.core.persistence.jpa.entity.task;
 
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Optional;
-import java.util.Set;
 import javax.persistence.CascadeType;
-import javax.persistence.CollectionTable;
-import javax.persistence.Column;
 import javax.persistence.DiscriminatorValue;
-import javax.persistence.ElementCollection;
 import javax.persistence.Entity;
 import javax.persistence.FetchType;
 import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
+import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
 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.jpa.entity.JPAImplementation;
 import org.apache.syncope.core.persistence.jpa.entity.JPARealm;
 
 @Entity
@@ -48,12 +48,13 @@ public class JPAPushTask extends AbstractProvisioningTask implements PushTask {
     @ManyToOne(fetch = FetchType.EAGER, optional = false)
     private JPARealm sourceRealm;
 
-    @ElementCollection(fetch = FetchType.EAGER)
-    @Column(name = "actionClassName")
-    @CollectionTable(name = "PushTask_actionsClassNames",
+    @ManyToMany(fetch = FetchType.EAGER)
+    @JoinTable(name = "PushTaskAction",
             joinColumns =
-            @JoinColumn(name = "pushTask_id", referencedColumnName = "id"))
-    private Set<String> actionsClassNames = new HashSet<>();
+            @JoinColumn(name = "task_id"),
+            inverseJoinColumns =
+            @JoinColumn(name = "implementation_id"))
+    private List<JPAImplementation> actions = new ArrayList<>();
 
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "pushTask")
     private List<JPAPushTaskAnyFilter> filters = new ArrayList<>();
@@ -70,8 +71,15 @@ public class JPAPushTask extends AbstractProvisioningTask implements PushTask {
     }
 
     @Override
-    public Set<String> getActionsClassNames() {
-        return actionsClassNames;
+    public boolean add(final Implementation action) {
+        checkType(action, JPAImplementation.class);
+        checkImplementationType(action, ImplementationType.PUSH_ACTIONS);
+        return actions.contains((JPAImplementation) action) || actions.add((JPAImplementation) action);
+    }
+
+    @Override
+    public List<? extends Implementation> getActions() {
+        return actions;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/d5b57922/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTask.java
----------------------------------------------------------------------
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 3a27e1b..e69d5ef 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
@@ -22,12 +22,16 @@ import java.util.Date;
 import javax.persistence.Basic;
 import javax.persistence.DiscriminatorValue;
 import javax.persistence.Entity;
+import javax.persistence.OneToOne;
 import javax.persistence.Temporal;
 import javax.persistence.TemporalType;
 import javax.validation.constraints.Max;
 import javax.validation.constraints.Min;
 import javax.validation.constraints.NotNull;
+import org.apache.syncope.common.lib.types.ImplementationType;
+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.jpa.entity.JPAImplementation;
 import org.apache.syncope.core.persistence.jpa.validation.entity.SchedTaskCheck;
 
 @Entity
@@ -42,7 +46,8 @@ public class JPASchedTask extends AbstractTask implements SchedTask {
 
     private String cronExpression;
 
-    private String jobDelegateClassName;
+    @OneToOne(optional = false)
+    private JPAImplementation jobDelegate;
 
     @NotNull
     private String name;
@@ -83,13 +88,15 @@ public class JPASchedTask extends AbstractTask implements SchedTask {
     }
 
     @Override
-    public String getJobDelegateClassName() {
-        return jobDelegateClassName;
+    public Implementation getJobDelegate() {
+        return jobDelegate;
     }
 
     @Override
-    public void setJobDelegateClassName(final String jobDelegateClassName) {
-        this.jobDelegateClassName = jobDelegateClassName;
+    public void setJobDelegate(final Implementation jobDelegate) {
+        checkType(jobDelegate, JPAImplementation.class);
+        checkImplementationType(jobDelegate, ImplementationType.TASKJOB_DELEGATE);
+        this.jobDelegate = (JPAImplementation) jobDelegate;
     }
 
     @Override