You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by md...@apache.org on 2015/08/27 13:12:49 UTC

[22/33] syncope git commit: Cleaning up implementation classes discovery

Cleaning up implementation classes discovery


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

Branch: refs/heads/SYNCOPE-156
Commit: 5da611de22243abc94021f4560667ad1d80a6991
Parents: 966484b
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Mon Aug 24 13:30:43 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Mon Aug 24 13:30:43 2015 +0200

----------------------------------------------------------------------
 .../apache/syncope/core/logic/SyncopeLogic.java |  27 ++-
 .../init/ClassPathScanImplementationLookup.java | 200 +++++++++++++++++++
 .../init/ImplementationClassNamesLoader.java    | 157 ---------------
 .../core/logic/report/AbstractReportlet.java    |   1 +
 .../core/logic/report/GroupReportlet.java       |   1 +
 .../core/logic/report/ReportJobDelegate.java    |  42 +---
 .../syncope/core/logic/report/Reportlet.java    |  40 ----
 .../core/logic/report/ReportletConfClass.java   |  32 ---
 .../core/logic/report/StaticReportlet.java      |   1 +
 .../core/logic/report/UserReportlet.java        |   1 +
 .../persistence/api/ImplementationLookup.java   |  53 +++++
 .../core/persistence/api/dao/Reportlet.java     |  40 ++++
 .../persistence/api/dao/ReportletConfClass.java |  32 +++
 .../core/persistence/jpa/dao/JPAUserDAO.java    |  77 ++-----
 .../jpa/DummyImplementationLookup.java          |  73 +++++++
 .../java/DummyImplementationLookup.java         |  73 +++++++
 16 files changed, 513 insertions(+), 337 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
index eb0c1ad..1354f67 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
@@ -18,8 +18,6 @@
  */
 package org.apache.syncope.core.logic;
 
-import static org.apache.syncope.core.logic.init.ImplementationClassNamesLoader.Type;
-
 import java.io.IOException;
 import java.lang.reflect.Method;
 import java.net.URI;
@@ -27,9 +25,10 @@ import java.util.HashSet;
 import java.util.Set;
 import javax.annotation.Resource;
 import org.apache.syncope.common.lib.to.SyncopeTO;
-import org.apache.syncope.core.logic.init.ImplementationClassNamesLoader;
 import org.apache.syncope.core.misc.security.PasswordGenerator;
 import org.apache.syncope.core.misc.spring.ResourceWithFallbackLoader;
+import org.apache.syncope.core.persistence.api.ImplementationLookup;
+import org.apache.syncope.core.persistence.api.ImplementationLookup.Type;
 import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager;
 import org.apache.syncope.core.provisioning.api.AnyTransformer;
@@ -87,7 +86,7 @@ public class SyncopeLogic extends AbstractLogic<SyncopeTO> {
     private PasswordGenerator passwordGenerator;
 
     @Autowired
-    private ImplementationClassNamesLoader classNamesLoader;
+    private ImplementationLookup implementationLookup;
 
     @Resource(name = "velocityResourceLoader")
     private ResourceWithFallbackLoader resourceLoader;
@@ -135,16 +134,16 @@ public class SyncopeLogic extends AbstractLogic<SyncopeTO> {
         syncopeTO.setVirAttrCache(virAttrCache.getClass().getName());
         syncopeTO.setPasswordGenerator(passwordGenerator.getClass().getName());
 
-        syncopeTO.getReportlets().addAll(classNamesLoader.getClassNames(Type.REPORTLET));
-        syncopeTO.getAccountRules().addAll(classNamesLoader.getClassNames(Type.ACCOUNT_RULE));
-        syncopeTO.getPasswordRules().addAll(classNamesLoader.getClassNames(Type.PASSWORD_RULE));
-        syncopeTO.getTaskJobs().addAll(classNamesLoader.getClassNames(Type.TASKJOBDELEGATE));
-        syncopeTO.getPropagationActions().addAll(classNamesLoader.getClassNames(Type.PROPAGATION_ACTIONS));
-        syncopeTO.getSyncActions().addAll(classNamesLoader.getClassNames(Type.SYNC_ACTIONS));
-        syncopeTO.getPushActions().addAll(classNamesLoader.getClassNames(Type.PUSH_ACTIONS));
-        syncopeTO.getSyncCorrelationRules().addAll(classNamesLoader.getClassNames(Type.SYNC_CORRELATION_RULE));
-        syncopeTO.getPushCorrelationRules().addAll(classNamesLoader.getClassNames(Type.PUSH_CORRELATION_RULE));
-        syncopeTO.getValidators().addAll(classNamesLoader.getClassNames(Type.VALIDATOR));
+        syncopeTO.getReportlets().addAll(implementationLookup.getClassNames(Type.REPORTLET));
+        syncopeTO.getAccountRules().addAll(implementationLookup.getClassNames(Type.ACCOUNT_RULE));
+        syncopeTO.getPasswordRules().addAll(implementationLookup.getClassNames(Type.PASSWORD_RULE));
+        syncopeTO.getTaskJobs().addAll(implementationLookup.getClassNames(Type.TASKJOBDELEGATE));
+        syncopeTO.getPropagationActions().addAll(implementationLookup.getClassNames(Type.PROPAGATION_ACTIONS));
+        syncopeTO.getSyncActions().addAll(implementationLookup.getClassNames(Type.SYNC_ACTIONS));
+        syncopeTO.getPushActions().addAll(implementationLookup.getClassNames(Type.PUSH_ACTIONS));
+        syncopeTO.getSyncCorrelationRules().addAll(implementationLookup.getClassNames(Type.SYNC_CORRELATION_RULE));
+        syncopeTO.getPushCorrelationRules().addAll(implementationLookup.getClassNames(Type.PUSH_CORRELATION_RULE));
+        syncopeTO.getValidators().addAll(implementationLookup.getClassNames(Type.VALIDATOR));
 
         Set<String> htmlTemplates = new HashSet<>();
         Set<String> textTemplates = new HashSet<>();

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java b/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
new file mode 100644
index 0000000..2305e70
--- /dev/null
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
@@ -0,0 +1,200 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic.init;
+
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.apache.commons.lang3.StringUtils;
+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.core.persistence.api.dao.Reportlet;
+import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
+import org.apache.syncope.core.persistence.api.ImplementationLookup;
+import org.apache.syncope.core.persistence.api.attrvalue.validation.Validator;
+import org.apache.syncope.core.persistence.api.dao.AccountRule;
+import org.apache.syncope.core.persistence.api.dao.AccountRuleConfClass;
+import org.apache.syncope.core.persistence.api.dao.PasswordRule;
+import org.apache.syncope.core.persistence.api.dao.PasswordRuleConfClass;
+import org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
+import org.apache.syncope.core.provisioning.api.sync.PushActions;
+import org.apache.syncope.core.provisioning.api.sync.SyncActions;
+import org.apache.syncope.core.provisioning.api.sync.SyncCorrelationRule;
+import org.apache.syncope.core.provisioning.java.sync.PushJobDelegate;
+import org.apache.syncope.core.provisioning.java.sync.SyncJobDelegate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
+import org.springframework.core.type.filter.AssignableTypeFilter;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ClassUtils;
+
+/**
+ * Cache class names for all implementations of Syncope interfaces found in classpath, for later usage.
+ */
+@Component
+public class ClassPathScanImplementationLookup implements ImplementationLookup {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ImplementationLookup.class);
+
+    private Map<Type, Set<String>> classNames;
+
+    private Map<Class<? extends ReportletConf>, Class<? extends Reportlet>> reportletClasses;
+
+    private Map<Class<? extends AccountRuleConf>, Class<? extends AccountRule>> accountRuleClasses;
+
+    private Map<Class<? extends PasswordRuleConf>, Class<? extends PasswordRule>> passwordRuleClasses;
+
+    @Override
+    public Integer getPriority() {
+        return 400;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void load() {
+        classNames = new EnumMap<>(Type.class);
+        for (Type type : Type.values()) {
+            classNames.put(type, new HashSet<String>());
+        }
+
+        reportletClasses = new HashMap<>();
+        accountRuleClasses = new HashMap<>();
+        passwordRuleClasses = new HashMap<>();
+
+        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
+        scanner.addIncludeFilter(new AssignableTypeFilter(Reportlet.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(AccountRule.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(PasswordRule.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(SchedTaskJobDelegate.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(SyncActions.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(PushActions.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(SyncCorrelationRule.class));
+        // Remove once SYNCOPE-470 is done
+        //scanner.addIncludeFilter(new AssignableTypeFilter(PushCorrelationRule.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(PropagationActions.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(Validator.class));
+
+        for (BeanDefinition bd : scanner.findCandidateComponents(StringUtils.EMPTY)) {
+            try {
+                Class<?> clazz = ClassUtils.resolveClassName(
+                        bd.getBeanClassName(), ClassUtils.getDefaultClassLoader());
+                boolean isAbsractClazz = Modifier.isAbstract(clazz.getModifiers());
+
+                if (Reportlet.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                    classNames.get(Type.REPORTLET).add(clazz.getName());
+
+                    ReportletConfClass annotation = clazz.getAnnotation(ReportletConfClass.class);
+                    if (annotation != null) {
+                        reportletClasses.put(annotation.value(), (Class<? extends Reportlet>) clazz);
+                    }
+                }
+
+                if (AccountRule.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                    classNames.get(Type.ACCOUNT_RULE).add(clazz.getName());
+
+                    AccountRuleConfClass annotation = clazz.getAnnotation(AccountRuleConfClass.class);
+                    if (annotation != null) {
+                        accountRuleClasses.put(annotation.value(), (Class<? extends AccountRule>) clazz);
+                    }
+                }
+                if (PasswordRule.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                    classNames.get(Type.PASSWORD_RULE).add(clazz.getName());
+                    PasswordRuleConfClass annotation = clazz.getAnnotation(PasswordRuleConfClass.class);
+                    if (annotation != null) {
+                        passwordRuleClasses.put(annotation.value(), (Class<? extends PasswordRule>) clazz);
+                    }
+                }
+
+                if (SchedTaskJobDelegate.class.isAssignableFrom(clazz) && !isAbsractClazz
+                        && !SyncJobDelegate.class.isAssignableFrom(clazz)
+                        && !PushJobDelegate.class.isAssignableFrom(clazz)) {
+
+                    classNames.get(Type.TASKJOBDELEGATE).add(bd.getBeanClassName());
+                }
+
+                if (SyncActions.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                    classNames.get(Type.SYNC_ACTIONS).add(bd.getBeanClassName());
+                }
+
+                if (PushActions.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                    classNames.get(Type.PUSH_ACTIONS).add(bd.getBeanClassName());
+                }
+
+                if (SyncCorrelationRule.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                    classNames.get(Type.SYNC_CORRELATION_RULE).add(bd.getBeanClassName());
+                }
+
+                // Uncomment when SYNCOPE-470 is done
+                /* if (PushCorrelationRule.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                 * classNames.get(Type.PUSH_CORRELATION_RULES).add(metadata.getClassName());
+                 * } */
+                if (PropagationActions.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                    classNames.get(Type.PROPAGATION_ACTIONS).add(bd.getBeanClassName());
+                }
+
+                if (Validator.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                    classNames.get(Type.VALIDATOR).add(bd.getBeanClassName());
+                }
+            } catch (Throwable t) {
+                LOG.warn("Could not inspect class {}", bd.getBeanClassName(), t);
+            }
+        }
+        classNames = Collections.unmodifiableMap(classNames);
+        reportletClasses = Collections.unmodifiableMap(reportletClasses);
+        accountRuleClasses = Collections.unmodifiableMap(accountRuleClasses);
+        passwordRuleClasses = Collections.unmodifiableMap(passwordRuleClasses);
+
+        LOG.debug("Implementation classes found: {}", classNames);
+    }
+
+    @Override
+    public Set<String> getClassNames(final Type type) {
+        return classNames.get(type);
+    }
+
+    @Override
+    public Class<? extends Reportlet> getReportletClass(
+            final Class<? extends ReportletConf> reportletConfClass) {
+
+        return reportletClasses.get(reportletConfClass);
+    }
+
+    @Override
+    public Class<? extends AccountRule> getAccountRuleClass(
+            final Class<? extends AccountRuleConf> accountRuleConfClass) {
+
+        return accountRuleClasses.get(accountRuleConfClass);
+    }
+
+    @Override
+    public Class<? extends PasswordRule> getPasswordRuleClass(
+            final Class<? extends PasswordRuleConf> passwordRuleConfClass) {
+
+        return passwordRuleClasses.get(passwordRuleConfClass);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/logic/src/main/java/org/apache/syncope/core/logic/init/ImplementationClassNamesLoader.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/init/ImplementationClassNamesLoader.java b/core/logic/src/main/java/org/apache/syncope/core/logic/init/ImplementationClassNamesLoader.java
deleted file mode 100644
index 7bc842e..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/init/ImplementationClassNamesLoader.java
+++ /dev/null
@@ -1,157 +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.logic.init;
-
-import java.lang.reflect.Modifier;
-import java.util.Collections;
-import java.util.EnumMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
-import org.apache.syncope.core.provisioning.api.sync.PushActions;
-import org.apache.syncope.core.provisioning.api.sync.SyncActions;
-import org.apache.syncope.core.provisioning.api.sync.SyncCorrelationRule;
-import org.apache.syncope.core.logic.report.Reportlet;
-import org.apache.syncope.core.persistence.api.SyncopeLoader;
-import org.apache.syncope.core.persistence.api.attrvalue.validation.Validator;
-import org.apache.syncope.core.persistence.api.dao.AccountRule;
-import org.apache.syncope.core.persistence.api.dao.PasswordRule;
-import org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate;
-import org.apache.syncope.core.provisioning.java.sync.PushJobDelegate;
-import org.apache.syncope.core.provisioning.java.sync.SyncJobDelegate;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.config.BeanDefinition;
-import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
-import org.springframework.core.type.filter.AssignableTypeFilter;
-import org.springframework.stereotype.Component;
-import org.springframework.util.ClassUtils;
-
-/**
- * Cache class names for all implementations of Syncope interfaces found in classpath, for later usage.
- */
-@Component
-public class ImplementationClassNamesLoader implements SyncopeLoader {
-
-    public enum Type {
-
-        REPORTLET,
-        ACCOUNT_RULE,
-        PASSWORD_RULE,
-        TASKJOBDELEGATE,
-        PROPAGATION_ACTIONS,
-        SYNC_ACTIONS,
-        PUSH_ACTIONS,
-        SYNC_CORRELATION_RULE,
-        PUSH_CORRELATION_RULE,
-        VALIDATOR
-
-    }
-
-    private static final Logger LOG = LoggerFactory.getLogger(ImplementationClassNamesLoader.class);
-
-    private Map<Type, Set<String>> classNames;
-
-    @Override
-    public Integer getPriority() {
-        return 400;
-    }
-
-    @Override
-    public void load() {
-        classNames = new EnumMap<>(Type.class);
-        for (Type type : Type.values()) {
-            classNames.put(type, new HashSet<String>());
-        }
-
-        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
-        scanner.addIncludeFilter(new AssignableTypeFilter(Reportlet.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(AccountRule.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(PasswordRule.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(SchedTaskJobDelegate.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(SyncActions.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(PushActions.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(SyncCorrelationRule.class));
-        // Remove once SYNCOPE-470 is done
-        //scanner.addIncludeFilter(new AssignableTypeFilter(PushCorrelationRule.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(PropagationActions.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(Validator.class));
-
-        for (BeanDefinition bd : scanner.findCandidateComponents(StringUtils.EMPTY)) {
-            try {
-                Class<?> clazz = ClassUtils.resolveClassName(
-                        bd.getBeanClassName(), ClassUtils.getDefaultClassLoader());
-                boolean isAbsractClazz = Modifier.isAbstract(clazz.getModifiers());
-
-                if (Reportlet.class.isAssignableFrom(clazz) && !isAbsractClazz) {
-                    classNames.get(Type.REPORTLET).add(clazz.getName());
-                }
-
-                if (AccountRule.class.isAssignableFrom(clazz) && !isAbsractClazz) {
-                    classNames.get(Type.ACCOUNT_RULE).add(clazz.getName());
-                }
-                if (PasswordRule.class.isAssignableFrom(clazz) && !isAbsractClazz) {
-                    classNames.get(Type.PASSWORD_RULE).add(clazz.getName());
-                }
-
-                if (SchedTaskJobDelegate.class.isAssignableFrom(clazz) && !isAbsractClazz
-                        && !SyncJobDelegate.class.isAssignableFrom(clazz)
-                        && !PushJobDelegate.class.isAssignableFrom(clazz)) {
-
-                    classNames.get(Type.TASKJOBDELEGATE).add(bd.getBeanClassName());
-                }
-
-                if (SyncActions.class.isAssignableFrom(clazz) && !isAbsractClazz) {
-                    classNames.get(Type.SYNC_ACTIONS).add(bd.getBeanClassName());
-                }
-
-                if (PushActions.class.isAssignableFrom(clazz) && !isAbsractClazz) {
-                    classNames.get(Type.PUSH_ACTIONS).add(bd.getBeanClassName());
-                }
-
-                if (SyncCorrelationRule.class.isAssignableFrom(clazz) && !isAbsractClazz) {
-                    classNames.get(Type.SYNC_CORRELATION_RULE).add(bd.getBeanClassName());
-                }
-
-                // Uncomment when SYNCOPE-470 is done
-                /* if (PushCorrelationRule.class.isAssignableFrom(clazz) && !isAbsractClazz) {
-                 * classNames.get(Type.PUSH_CORRELATION_RULES).add(metadata.getClassName());
-                 * } */
-                if (PropagationActions.class.isAssignableFrom(clazz) && !isAbsractClazz) {
-                    classNames.get(Type.PROPAGATION_ACTIONS).add(bd.getBeanClassName());
-                }
-
-                if (Validator.class.isAssignableFrom(clazz) && !isAbsractClazz) {
-                    classNames.get(Type.VALIDATOR).add(bd.getBeanClassName());
-                }
-            } catch (Throwable t) {
-                LOG.warn("Could not inspect class {}", bd.getBeanClassName(), t);
-            }
-        }
-        classNames = Collections.unmodifiableMap(classNames);
-
-        LOG.debug("Implementation classes found: {}", classNames);
-    }
-
-    public Set<String> getClassNames(final Type type) {
-        return classNames.get(type);
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java
index 482ad0e..69e6b4f 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.core.logic.report;
 
+import org.apache.syncope.core.persistence.api.dao.Reportlet;
 import org.apache.syncope.common.lib.report.ReportletConf;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
index c5e6642..ec9a745 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.core.logic.report;
 
+import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java
index d790568..2b63d1b 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java
@@ -20,10 +20,7 @@ package org.apache.syncope.core.logic.report;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.lang.reflect.Modifier;
 import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
 import java.util.zip.Deflater;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
@@ -33,14 +30,15 @@ import javax.xml.transform.sax.SAXTransformerFactory;
 import javax.xml.transform.sax.TransformerHandler;
 import javax.xml.transform.stream.StreamResult;
 import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.common.lib.types.ReportExecStatus;
 import org.apache.syncope.core.misc.ExceptionUtils2;
 import org.apache.syncope.core.misc.spring.ApplicationContextProvider;
+import org.apache.syncope.core.persistence.api.ImplementationLookup;
 import org.apache.syncope.core.persistence.api.dao.ReportDAO;
 import org.apache.syncope.core.persistence.api.dao.ReportExecDAO;
+import org.apache.syncope.core.persistence.api.dao.Reportlet;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.Report;
 import org.apache.syncope.core.persistence.api.entity.ReportExec;
@@ -48,13 +46,9 @@ import org.quartz.JobExecutionException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.beans.factory.support.AbstractBeanDefinition;
-import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
-import org.springframework.core.type.filter.AssignableTypeFilter;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
-import org.springframework.util.ClassUtils;
 import org.xml.sax.helpers.AttributesImpl;
 
 @Component
@@ -62,32 +56,6 @@ public class ReportJobDelegate {
 
     private static final Logger LOG = LoggerFactory.getLogger(ReportJobDelegate.class);
 
-    private static final Map<Class<? extends ReportletConf>, Class<Reportlet>> REPORTLET_CLASSES = new HashMap<>();
-
-    static {
-        initReportletClasses();
-    }
-
-    @SuppressWarnings("unchecked")
-    private static void initReportletClasses() {
-        ClassPathScanningCandidateComponentProvider scanner =
-                new ClassPathScanningCandidateComponentProvider(false);
-        scanner.addIncludeFilter(new AssignableTypeFilter(Reportlet.class));
-
-        for (BeanDefinition bd : scanner.findCandidateComponents(StringUtils.EMPTY)) {
-            Class<?> clazz = ClassUtils.resolveClassName(
-                    bd.getBeanClassName(), ClassUtils.getDefaultClassLoader());
-            boolean isAbstract = Modifier.isAbstract(clazz.getModifiers());
-
-            if (Reportlet.class.isAssignableFrom(clazz) && !isAbstract) {
-                ReportletConfClass annotation = clazz.getAnnotation(ReportletConfClass.class);
-                if (annotation != null) {
-                    REPORTLET_CLASSES.put(annotation.value(), (Class<Reportlet>) clazz);
-                }
-            }
-        }
-    }
-
     /**
      * Report DAO.
      */
@@ -103,6 +71,9 @@ public class ReportJobDelegate {
     @Autowired
     private EntityFactory entityFactory;
 
+    @Autowired
+    private ImplementationLookup implementationLookup;
+
     @Transactional
     public void execute(final Long reportKey) throws JobExecutionException {
         Report report = reportDAO.find(reportKey);
@@ -156,7 +127,8 @@ public class ReportJobDelegate {
 
             // iterate over reportlet instances defined for this report
             for (ReportletConf reportletConf : report.getReportletConfs()) {
-                Class<Reportlet> reportletClass = REPORTLET_CLASSES.get(reportletConf.getClass());
+                Class<? extends Reportlet> reportletClass =
+                        implementationLookup.getReportletClass(reportletConf.getClass());
                 if (reportletClass == null) {
                     LOG.warn("Could not find matching reportlet for {}", reportletConf.getClass());
                 } else {

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/logic/src/main/java/org/apache/syncope/core/logic/report/Reportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/Reportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/Reportlet.java
deleted file mode 100644
index 66d00c4..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/Reportlet.java
+++ /dev/null
@@ -1,40 +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.logic.report;
-
-import org.apache.syncope.common.lib.report.ReportletConf;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-
-/**
- * Interface for all elements that can be embedded in a report.
- *
- * @see org.apache.syncope.core.persistence.api.entity.Report
- */
-public interface Reportlet {
-
-    /**
-     * Actual data extraction for reporting.
-     *
-     * @param conf configuration
-     * @param handler SAX content handler for streaming result
-     * @throws SAXException if there is any problem in SAX handling
-     */
-    void extract(ReportletConf conf, ContentHandler handler) throws SAXException;
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportletConfClass.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportletConfClass.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportletConfClass.java
deleted file mode 100644
index 39f1c16..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportletConfClass.java
+++ /dev/null
@@ -1,32 +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.logic.report;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import org.apache.syncope.common.lib.report.ReportletConf;
-
-@Target({ ElementType.TYPE })
-@Retention(RetentionPolicy.RUNTIME)
-public @interface ReportletConfClass {
-
-    Class<? extends ReportletConf> value();
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java
index 963d484..b7db39f 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.core.logic.report;
 
+import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
 import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.common.lib.report.StaticReportletConf;
 import org.apache.syncope.core.misc.DataFormat;

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
index 44ae0e0..91715a7 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.core.logic.report;
 
+import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/ImplementationLookup.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/ImplementationLookup.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/ImplementationLookup.java
new file mode 100644
index 0000000..b00d3d4
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/ImplementationLookup.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api;
+
+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.core.persistence.api.dao.AccountRule;
+import org.apache.syncope.core.persistence.api.dao.PasswordRule;
+import org.apache.syncope.core.persistence.api.dao.Reportlet;
+
+public interface ImplementationLookup extends SyncopeLoader {
+
+    public enum Type {
+
+        REPORTLET,
+        ACCOUNT_RULE,
+        PASSWORD_RULE,
+        TASKJOBDELEGATE,
+        PROPAGATION_ACTIONS,
+        SYNC_ACTIONS,
+        PUSH_ACTIONS,
+        SYNC_CORRELATION_RULE,
+        PUSH_CORRELATION_RULE,
+        VALIDATOR
+
+    }
+
+    Set<String> getClassNames(Type type);
+
+    Class<? extends Reportlet> getReportletClass(Class<? extends ReportletConf> reportletConfClass);
+
+    Class<? extends AccountRule> getAccountRuleClass(Class<? extends AccountRuleConf> accountRuleConfClass);
+
+    Class<? extends PasswordRule> getPasswordRuleClass(Class<? extends PasswordRuleConf> passwordRuleConfClass);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/Reportlet.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/Reportlet.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/Reportlet.java
new file mode 100644
index 0000000..8180831
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/Reportlet.java
@@ -0,0 +1,40 @@
+/*
+ * 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.api.dao;
+
+import org.apache.syncope.common.lib.report.ReportletConf;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * Interface for all elements that can be embedded in a report.
+ *
+ * @see org.apache.syncope.core.persistence.api.entity.Report
+ */
+public interface Reportlet {
+
+    /**
+     * Actual data extraction for reporting.
+     *
+     * @param conf configuration
+     * @param handler SAX content handler for streaming result
+     * @throws SAXException if there is any problem in SAX handling
+     */
+    void extract(ReportletConf conf, ContentHandler handler) throws SAXException;
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ReportletConfClass.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ReportletConfClass.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ReportletConfClass.java
new file mode 100644
index 0000000..c1b3040
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ReportletConfClass.java
@@ -0,0 +1,32 @@
+/*
+ * 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.api.dao;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.apache.syncope.common.lib.report.ReportletConf;
+
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ReportletConfClass {
+
+    Class<? extends ReportletConf> value();
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/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 130c01e..bdc552e 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
@@ -18,13 +18,10 @@
  */
 package org.apache.syncope.core.persistence.jpa.dao;
 
-import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import javax.annotation.Resource;
 import javax.persistence.NoResultException;
@@ -32,93 +29,50 @@ import javax.persistence.TypedQuery;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Predicate;
 import org.apache.commons.collections4.Transformer;
-import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.ImmutablePair;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.policy.AccountRuleConf;
+import org.apache.syncope.common.lib.policy.PasswordRuleConf;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.Entitlement;
 import org.apache.syncope.common.lib.types.EntityViolationType;
-import org.apache.syncope.common.lib.policy.PasswordRuleConf;
 import org.apache.syncope.core.misc.policy.AccountPolicyException;
 import org.apache.syncope.core.misc.policy.PasswordPolicyException;
-import org.apache.syncope.core.persistence.api.dao.NotFoundException;
-import org.apache.syncope.core.persistence.api.dao.GroupDAO;
-import org.apache.syncope.core.persistence.api.dao.UserDAO;
-import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
-import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion;
-import org.apache.syncope.core.persistence.api.entity.user.User;
-import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
 import org.apache.syncope.core.misc.security.AuthContextUtils;
 import org.apache.syncope.core.misc.security.UnauthorizedException;
 import org.apache.syncope.core.misc.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.AccountRule;
-import org.apache.syncope.core.persistence.api.dao.AccountRuleConfClass;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.dao.PasswordRule;
-import org.apache.syncope.core.persistence.api.dao.PasswordRuleConfClass;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
 import org.apache.syncope.core.persistence.api.dao.RoleDAO;
-import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
-import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 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;
+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.resource.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion;
 import org.apache.syncope.core.persistence.api.entity.user.UMembership;
+import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.persistence.jpa.entity.JPAAnyUtilsFactory;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPADynRoleMembership;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUDynGroupMembership;
+import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.beans.factory.support.AbstractBeanDefinition;
-import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
-import org.springframework.core.type.filter.AssignableTypeFilter;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
-import org.springframework.util.ClassUtils;
 
 @Repository
 public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
 
-    private static final Map<Class<? extends AccountRuleConf>, Class<AccountRule>> ACCOUNT_RULES_CLASSES =
-            new HashMap<>();
-
-    private static final Map<Class<? extends PasswordRuleConf>, Class<PasswordRule>> PASSWORD_RULES_CLASSES =
-            new HashMap<>();
-
-    static {
-        initRules();
-    }
-
-    @SuppressWarnings("unchecked")
-    private static void initRules() {
-        ClassPathScanningCandidateComponentProvider scanner =
-                new ClassPathScanningCandidateComponentProvider(false);
-        scanner.addIncludeFilter(new AssignableTypeFilter(AccountRule.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(PasswordRule.class));
-
-        for (BeanDefinition bd : scanner.findCandidateComponents(StringUtils.EMPTY)) {
-            Class<?> clazz = ClassUtils.resolveClassName(
-                    bd.getBeanClassName(), ClassUtils.getDefaultClassLoader());
-            boolean isAbstract = Modifier.isAbstract(clazz.getModifiers());
-
-            if (AccountRule.class.isAssignableFrom(clazz) && !isAbstract) {
-                AccountRuleConfClass annotation = clazz.getAnnotation(AccountRuleConfClass.class);
-                if (annotation != null) {
-                    ACCOUNT_RULES_CLASSES.put(annotation.value(), (Class<AccountRule>) clazz);
-                }
-            }
-            if (PasswordRule.class.isAssignableFrom(clazz) && !isAbstract) {
-                PasswordRuleConfClass annotation = clazz.getAnnotation(PasswordRuleConfClass.class);
-                if (annotation != null) {
-                    PASSWORD_RULES_CLASSES.put(annotation.value(), (Class<PasswordRule>) clazz);
-                }
-            }
-        }
-    }
-
     @Autowired
     private RealmDAO realmDAO;
 
@@ -128,6 +82,9 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
     @Autowired
     private RoleDAO roleDAO;
 
+    @Autowired
+    private ImplementationLookup implementationLookup;
+
     @Resource(name = "adminUser")
     private String adminUser;
 
@@ -280,7 +237,8 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
                 }
 
                 for (PasswordRuleConf ruleConf : policy.getRuleConfs()) {
-                    Class<PasswordRule> ruleClass = PASSWORD_RULES_CLASSES.get(ruleConf.getClass());
+                    Class<? extends PasswordRule> ruleClass =
+                            implementationLookup.getPasswordRuleClass(ruleConf.getClass());
                     if (ruleClass == null) {
                         LOG.warn("Could not find matching password rule for {}", ruleConf.getClass());
                     } else {
@@ -342,7 +300,8 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
 
             for (AccountPolicy policy : getAccountPolicies(user)) {
                 for (AccountRuleConf ruleConf : policy.getRuleConfs()) {
-                    Class<AccountRule> ruleClass = ACCOUNT_RULES_CLASSES.get(ruleConf.getClass());
+                    Class<? extends AccountRule> ruleClass =
+                            implementationLookup.getAccountRuleClass(ruleConf.getClass());
                     if (ruleClass == null) {
                         LOG.warn("Could not find matching password rule for {}", ruleConf.getClass());
                     } else {

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/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
new file mode 100644
index 0000000..dd761b4
--- /dev/null
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyImplementationLookup.java
@@ -0,0 +1,73 @@
+/*
+ * 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;
+
+import java.util.Collections;
+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.core.persistence.api.ImplementationLookup;
+import org.apache.syncope.core.persistence.api.dao.AccountRule;
+import org.apache.syncope.core.persistence.api.dao.PasswordRule;
+import org.apache.syncope.core.persistence.api.dao.Reportlet;
+import org.apache.syncope.core.persistence.jpa.dao.DefaultAccountRule;
+import org.apache.syncope.core.persistence.jpa.dao.DefaultPasswordRule;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DummyImplementationLookup implements ImplementationLookup {
+
+    @Override
+    public Integer getPriority() {
+        return -1;
+    }
+
+    @Override
+    public void load() {
+        // do nothing
+    }
+
+    @Override
+    public Set<String> getClassNames(final Type type) {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public Class<Reportlet> getReportletClass(
+            final Class<? extends ReportletConf> reportletConfClass) {
+
+        return null;
+    }
+
+    @Override
+    public Class<? extends AccountRule> getAccountRuleClass(
+            final Class<? extends AccountRuleConf> accountRuleConfClass) {
+
+        return DefaultAccountRule.class;
+    }
+
+    @Override
+    public Class<? extends PasswordRule> getPasswordRuleClass(
+            final Class<? extends PasswordRuleConf> passwordRuleConfClass) {
+
+        return DefaultPasswordRule.class;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DummyImplementationLookup.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DummyImplementationLookup.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DummyImplementationLookup.java
new file mode 100644
index 0000000..06715d8
--- /dev/null
+++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DummyImplementationLookup.java
@@ -0,0 +1,73 @@
+/*
+ * 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.provisioning.java;
+
+import java.util.Collections;
+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.core.persistence.api.ImplementationLookup;
+import org.apache.syncope.core.persistence.api.dao.AccountRule;
+import org.apache.syncope.core.persistence.api.dao.PasswordRule;
+import org.apache.syncope.core.persistence.api.dao.Reportlet;
+import org.apache.syncope.core.persistence.jpa.dao.DefaultAccountRule;
+import org.apache.syncope.core.persistence.jpa.dao.DefaultPasswordRule;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DummyImplementationLookup implements ImplementationLookup {
+
+    @Override
+    public Integer getPriority() {
+        return -1;
+    }
+
+    @Override
+    public void load() {
+        // do nothing
+    }
+
+    @Override
+    public Set<String> getClassNames(final Type type) {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public Class<Reportlet> getReportletClass(
+            final Class<? extends ReportletConf> reportletConfClass) {
+
+        return null;
+    }
+
+    @Override
+    public Class<? extends AccountRule> getAccountRuleClass(
+            final Class<? extends AccountRuleConf> accountRuleConfClass) {
+
+        return DefaultAccountRule.class;
+    }
+
+    @Override
+    public Class<? extends PasswordRule> getPasswordRuleClass(
+            final Class<? extends PasswordRuleConf> passwordRuleConfClass) {
+
+        return DefaultPasswordRule.class;
+    }
+
+}