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:37:03 UTC

[14/18] syncope git commit: [SYNCOPE-956] Console implementation

[SYNCOPE-956] Console implementation


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

Branch: refs/heads/master
Commit: 9889072252105c0f3c029ae188a12ead3aff542f
Parents: f08a17b
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Fri Oct 6 17:07:52 2017 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Tue Oct 10 08:34:14 2017 +0200

----------------------------------------------------------------------
 .../commands/schema/SchemaResultManager.java    |   2 +-
 .../client/console/commons/Constants.java       |   2 +
 .../init/ClassPathScanImplementationLookup.java |  48 ++-
 .../NotificationDirectoryPanel.java             |   4 +-
 .../NotificationWizardBuilder.java              |  35 +-
 .../syncope/client/console/pages/Audit.java     |  27 +-
 .../syncope/client/console/pages/BasePage.java  |   6 +
 .../client/console/pages/Implementations.java   |  66 ++++
 .../panels/ImplementationDirectoryPanel.java    | 232 +++++++++++++
 .../panels/ImplementationEngineTogglePanel.java |  84 +++++
 .../panels/ImplementationModalPanel.java        | 329 +++++++++++++++++++
 .../panels/ParametersEditModalPanel.java        |   9 +-
 .../console/panels/PlainSchemaDetails.java      |  29 +-
 .../client/console/panels/RealmDetails.java     |  36 +-
 .../policies/AccountPolicyDirectoryPanel.java   |   2 +-
 .../policies/PasswordPolicyDirectoryPanel.java  |   2 +-
 .../policies/PolicyRuleDirectoryPanel.java      |  65 +++-
 .../policies/PolicyRuleWizardBuilder.java       | 153 ++++-----
 .../console/policies/PolicyRuleWrapper.java     |  29 +-
 .../console/policies/PolicySpecModalPanel.java  |  82 ++---
 .../policies/PullPolicyDirectoryPanel.java      |   2 +-
 .../reports/ReportletDirectoryPanel.java        |  57 +++-
 .../console/reports/ReportletWizardBuilder.java | 113 ++-----
 .../console/reports/ReportletWrapper.java       |  33 +-
 .../console/rest/ImplementationRestClient.java  |  18 +-
 .../console/tasks/SchedTaskDirectoryPanel.java  |   4 +-
 .../console/tasks/SchedTaskWizardBuilder.java   | 104 +++---
 .../html/form/ActionLinksTogglePanel.java       |   6 +-
 .../client/console/wizards/any/Ownership.java   |   4 +-
 .../console/wizards/any/Relationships.java      |   4 +-
 .../client/console/wizards/any/Resources.java   |   4 +-
 .../resources/AbstractConnConfPanel.java        |   6 +-
 .../resources/ItemTransformersTogglePanel.java  |  22 +-
 .../resources/ObjectTypeTogglePanel.java        |   8 +-
 .../resources/ProvisionWizardBuilder.java       |   3 +-
 .../wizards/resources/ResourceDetailsPanel.java |  26 +-
 .../src/main/resources/console.properties       |   1 +
 .../SyncopeConsoleApplication.properties        |   1 +
 .../SyncopeConsoleApplication_it.properties     |   1 +
 .../SyncopeConsoleApplication_pt_BR.properties  |   1 +
 .../SyncopeConsoleApplication_ru.properties     |   3 +-
 .../implementations/MyAccountRule.groovy        |  29 ++
 .../implementations/MyItemTransformer.groovy    |  46 +++
 .../implementations/MyLogicActions.groovy       |  38 +++
 .../implementations/MyPasswordRule.groovy       |  28 ++
 .../implementations/MyPropagationActions.groovy |  43 +++
 .../implementations/MyPullActions.groovy        | 125 +++++++
 .../MyPullCorrelationRule.groovy                |  31 ++
 .../implementations/MyPushActions.groovy        | 112 +++++++
 .../implementations/MyRecipientsProvider.groovy |  31 ++
 .../implementations/MyReconFilterBuilder.groovy |  30 ++
 .../console/implementations/MyReportlet.groovy  |  30 ++
 .../MySchedTaskJobDelegate.groovy               |  31 ++
 .../console/implementations/MyValidator.groovy  |  34 ++
 .../NotificationWizardBuilder$Recipients.html   |  47 ++-
 ...ificationWizardBuilder$Recipients.properties |   2 +-
 ...cationWizardBuilder$Recipients_it.properties |   2 +-
 ...ionWizardBuilder$Recipients_pt_BR.properties |   2 +-
 ...cationWizardBuilder$Recipients_ru.properties |   2 +-
 .../syncope/client/console/pages/BasePage.html  |   1 +
 .../client/console/pages/Implementations.html   |  36 ++
 .../panels/ImplementationDirectoryPanel.html    |  23 ++
 .../ImplementationDirectoryPanel.properties     |  19 ++
 .../ImplementationDirectoryPanel_it.properties  |  19 ++
 ...mplementationDirectoryPanel_pt_BR.properties |  19 ++
 .../ImplementationDirectoryPanel_ru.properties  |  20 ++
 .../panels/ImplementationEngineTogglePanel.html |  32 ++
 .../panels/ImplementationModalPanel.html        |  62 ++++
 .../console/panels/PlainSchemaDetails.html      |   2 +-
 .../panels/PlainSchemaDetails.properties        |   2 +-
 .../panels/PlainSchemaDetails_it.properties     |   2 +-
 .../panels/PlainSchemaDetails_pt_BR.properties  |   2 +-
 .../panels/PlainSchemaDetails_ru.properties     |  36 +-
 .../client/console/panels/RealmDetails.html     |   2 +-
 .../console/panels/RealmDetails.properties      |   2 +-
 .../console/panels/RealmDetails_it.properties   |   2 +-
 .../panels/RealmDetails_pt_BR.properties        |   2 +-
 .../console/panels/RealmDetails_ru.properties   |   2 +-
 .../policies/PolicyDirectoryPanel.properties    |   3 +-
 .../policies/PolicyDirectoryPanel_it.properties |   3 +-
 .../PolicyDirectoryPanel_pt_BR.properties       |   3 +-
 .../policies/PolicyDirectoryPanel_ru.properties |   3 +-
 .../PolicyRuleDirectoryPanel.properties         |   5 +-
 .../PolicyRuleDirectoryPanel_it.properties      |   5 +-
 .../PolicyRuleDirectoryPanel_pt_BR.properties   |   5 +-
 .../PolicyRuleDirectoryPanel_ru.properties      |   5 +-
 .../PolicyRuleWizardBuilder$Profile.html        |   5 +-
 ...licySpecModalPanel$CorrelationRulePanel.html |   2 +-
 .../policies/PolicySpecModalPanel.properties    |   4 +-
 .../policies/PolicySpecModalPanel_it.properties |   4 +-
 .../PolicySpecModalPanel_pt_BR.properties       |   4 +-
 .../policies/PolicySpecModalPanel_ru.properties |   4 +-
 .../reports/ReportletDirectoryPanel.properties  |   1 +
 .../ReportletDirectoryPanel_it.properties       |   1 +
 .../ReportletDirectoryPanel_pt_BR.properties    |   1 +
 .../ReportletDirectoryPanel_ru.properties       |   1 +
 .../reports/ReportletWizardBuilder$Profile.html |   3 +-
 .../ReportletWizardBuilder$Profile.properties   |   2 +-
 ...ReportletWizardBuilder$Profile_it.properties |   2 +-
 ...ortletWizardBuilder$Profile_pt_BR.properties |   2 +-
 ...ReportletWizardBuilder$Profile_ru.properties |   2 +-
 .../tasks/SchedTaskDirectoryPanel.properties    |   6 +-
 .../tasks/SchedTaskDirectoryPanel_it.properties |   6 +-
 .../SchedTaskDirectoryPanel_pt_BR.properties    |   6 +-
 .../tasks/SchedTaskDirectoryPanel_ru.properties |   9 +-
 .../tasks/SchedTaskWizardBuilder$Profile.html   |   6 +-
 .../SchedTaskWizardBuilder$Profile.properties   |   2 +-
 ...SchedTaskWizardBuilder$Profile_it.properties |   2 +-
 ...edTaskWizardBuilder$Profile_pt_BR.properties |   2 +-
 ...SchedTaskWizardBuilder$Profile_ru.properties |   2 +-
 .../ObjectTypeTogglePanel_it.properties         |   4 +-
 .../wizards/resources/ResourceDetailsPanel.html |   2 +-
 .../resources/ResourceDetailsPanel.properties   |   2 +-
 .../ResourceDetailsPanel_it.properties          |   2 +-
 .../ResourceDetailsPanel_pt_BR.properties       |   2 +-
 .../ResourceDetailsPanel_ru.properties          |   2 +-
 .../lib/report/ReconciliationReportletConf.java |   1 -
 .../syncope/common/lib/to/PlainSchemaTO.java    |  10 +-
 .../syncope/core/logic/ImplementationLogic.java |  14 +
 .../core/persistence/api/dao/AccountRule.java   |   3 +-
 .../core/persistence/api/dao/PasswordRule.java  |   7 +-
 .../core/persistence/api/dao/Reportlet.java     |   3 +-
 .../test/resources/domains/MasterContent.xml    |   4 +-
 .../java/data/ImplementationDataBinderImpl.java |  12 +-
 .../java/data/SchemaDataBinderImpl.java         |  22 +-
 .../console/panels/SAML2IdPsDirectoryPanel.java |   4 +-
 126 files changed, 2197 insertions(+), 619 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/98890722/client/cli/src/main/java/org/apache/syncope/client/cli/commands/schema/SchemaResultManager.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/schema/SchemaResultManager.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/schema/SchemaResultManager.java
index 95d5628..4da9446 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/schema/SchemaResultManager.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/schema/SchemaResultManager.java
@@ -61,7 +61,7 @@ public class SchemaResultManager extends CommonsResultManager {
         System.out.println("    conversion pattern: " + schemaTO.getConversionPattern());
         System.out.println("    mandatory condition: " + schemaTO.getMandatoryCondition());
         System.out.println("    mime type: " + schemaTO.getMimeType());
-        System.out.println("    validator class: " + schemaTO.getValidatorClass());
+        System.out.println("    validator class: " + schemaTO.getValidator());
         System.out.println("    cipher algorithm: " + (schemaTO.getCipherAlgorithm() == null
                 ? "" : schemaTO.getCipherAlgorithm().getAlgorithm()));
         System.out.println("");

http://git-wip-us.apache.org/repos/asf/syncope/blob/98890722/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java b/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
index 5449097..c71fdbc 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
@@ -121,6 +121,8 @@ public final class Constants {
 
     public static final String PREF_NOTIFICATION_PAGINATOR_ROWS = "notification.paginator.rows";
 
+    public static final String PREF_IMPLEMENTATION_PAGINATOR_ROWS = "implementation.paginator.rows";
+
     public static final String PREF_MAIL_TEMPLATE_PAGINATOR_ROWS = "mail.template.paginator.rows";
 
     public static final String PREF_PROPAGATION_TASKS_PAGINATOR_ROWS = "proagationtasks.paginator.rows";

http://git-wip-us.apache.org/repos/asf/syncope/blob/98890722/client/console/src/main/java/org/apache/syncope/client/console/init/ClassPathScanImplementationLookup.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/init/ClassPathScanImplementationLookup.java b/client/console/src/main/java/org/apache/syncope/client/console/init/ClassPathScanImplementationLookup.java
index 7a39743..ed60e6c 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/init/ClassPathScanImplementationLookup.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/init/ClassPathScanImplementationLookup.java
@@ -21,7 +21,9 @@ package org.apache.syncope.client.console.init;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.ObjectUtils;
 import org.apache.syncope.client.console.pages.BaseExtPage;
@@ -32,6 +34,9 @@ import org.apache.syncope.client.console.pages.BasePage;
 import org.apache.syncope.client.console.panels.SSOLoginFormPanel;
 import org.apache.syncope.client.console.wicket.markup.html.form.preview.AbstractBinaryPreviewer;
 import org.apache.syncope.client.console.widgets.BaseExtWidget;
+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.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
@@ -42,7 +47,7 @@ public class ClassPathScanImplementationLookup {
 
     private static final Logger LOG = LoggerFactory.getLogger(ClassPathScanImplementationLookup.class);
 
-    private static final String DEFAULT_BASE_PACKAGE = "org.apache.syncope.client.console";
+    private static final String DEFAULT_BASE_PACKAGE = "org.apache.syncope";
 
     private List<Class<? extends BasePage>> pages;
 
@@ -54,6 +59,12 @@ public class ClassPathScanImplementationLookup {
 
     private List<Class<? extends SSOLoginFormPanel>> ssoLoginFormPanels;
 
+    private Map<String, Class<? extends ReportletConf>> reportletConfs;
+
+    private Map<String, Class<? extends AccountRuleConf>> accountRuleConfs;
+
+    private Map<String, Class<? extends PasswordRuleConf>> passwordRuleConfs;
+
     /**
      * This method can be overridden by subclasses to customize classpath scan.
      *
@@ -70,6 +81,9 @@ public class ClassPathScanImplementationLookup {
         extPages = new ArrayList<>();
         extWidgets = new ArrayList<>();
         ssoLoginFormPanels = new ArrayList<>();
+        reportletConfs = new HashMap<>();
+        accountRuleConfs = new HashMap<>();
+        passwordRuleConfs = new HashMap<>();
 
         ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
         scanner.addIncludeFilter(new AssignableTypeFilter(BasePage.class));
@@ -77,6 +91,9 @@ public class ClassPathScanImplementationLookup {
         scanner.addIncludeFilter(new AssignableTypeFilter(BaseExtPage.class));
         scanner.addIncludeFilter(new AssignableTypeFilter(BaseExtWidget.class));
         scanner.addIncludeFilter(new AssignableTypeFilter(SSOLoginFormPanel.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(ReportletConf.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(AccountRuleConf.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(PasswordRuleConf.class));
 
         scanner.findCandidateComponents(getBasePackage()).forEach(bd -> {
             try {
@@ -105,6 +122,12 @@ public class ClassPathScanImplementationLookup {
                         previewers.add((Class<? extends AbstractBinaryPreviewer>) clazz);
                     } else if (SSOLoginFormPanel.class.isAssignableFrom(clazz)) {
                         ssoLoginFormPanels.add((Class<? extends SSOLoginFormPanel>) clazz);
+                    } else if (ReportletConf.class.isAssignableFrom(clazz)) {
+                        reportletConfs.put(clazz.getName(), (Class<? extends ReportletConf>) clazz);
+                    } else if (AccountRuleConf.class.isAssignableFrom(clazz)) {
+                        accountRuleConfs.put(clazz.getName(), (Class<? extends AccountRuleConf>) clazz);
+                    } else if (PasswordRuleConf.class.isAssignableFrom(clazz)) {
+                        passwordRuleConfs.put(clazz.getName(), (Class<? extends PasswordRuleConf>) clazz);
                     }
                 }
             } catch (Throwable t) {
@@ -128,10 +151,17 @@ public class ClassPathScanImplementationLookup {
 
         ssoLoginFormPanels = Collections.unmodifiableList(ssoLoginFormPanels);
 
+        reportletConfs = Collections.unmodifiableMap(reportletConfs);
+        accountRuleConfs = Collections.unmodifiableMap(accountRuleConfs);
+        passwordRuleConfs = Collections.unmodifiableMap(passwordRuleConfs);
+
         LOG.debug("Binary previewers found: {}", previewers);
         LOG.debug("Extension pages found: {}", extPages);
         LOG.debug("Extension widgets found: {}", extWidgets);
         LOG.debug("SSO Login pages found: {}", ssoLoginFormPanels);
+        LOG.debug("Reportlet configurations found: {}", reportletConfs);
+        LOG.debug("Account Rule configurations found: {}", accountRuleConfs);
+        LOG.debug("Password Rule configurations found: {}", passwordRuleConfs);
     }
 
     public Class<? extends AbstractBinaryPreviewer> getPreviewerClass(final String mimeType) {
@@ -152,10 +182,6 @@ public class ClassPathScanImplementationLookup {
         return pages;
     }
 
-    public List<Class<? extends AbstractBinaryPreviewer>> getPreviewerClasses() {
-        return previewers;
-    }
-
     public List<Class<? extends BaseExtPage>> getExtPageClasses() {
         return extPages;
     }
@@ -168,4 +194,16 @@ public class ClassPathScanImplementationLookup {
         return ssoLoginFormPanels;
     }
 
+    public Map<String, Class<? extends ReportletConf>> getReportletConfs() {
+        return reportletConfs;
+    }
+
+    public Map<String, Class<? extends AccountRuleConf>> getAccountRuleConfs() {
+        return accountRuleConfs;
+    }
+
+    public Map<String, Class<? extends PasswordRuleConf>> getPasswordRuleConfs() {
+        return passwordRuleConfs;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/98890722/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationDirectoryPanel.java
index cb33f80..bdc9269 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationDirectoryPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationDirectoryPanel.java
@@ -141,8 +141,8 @@ public class NotificationDirectoryPanel
                     target.add(container);
                 } catch (SyncopeClientException e) {
                     LOG.error("While deleting object {}", model.getObject().getKey(), e);
-                    SyncopeConsoleSession.get().error(StringUtils.isBlank(e.getMessage()) ? e.getClass().
-                            getName() : e.getMessage());
+                    SyncopeConsoleSession.get().error(
+                            StringUtils.isBlank(e.getMessage()) ? e.getClass().getName() : e.getMessage());
                 }
                 ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
             }

http://git-wip-us.apache.org/repos/asf/syncope/blob/98890722/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java
index 9e9097a..3602b44 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java
@@ -18,23 +18,22 @@
  */
 package org.apache.syncope.client.console.notifications;
 
-import org.apache.syncope.client.console.events.EventCategoryPanel;
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
-import java.util.Optional;
 import java.util.stream.Collectors;
 import org.apache.commons.lang3.tuple.Pair;
-import org.apache.syncope.client.console.SyncopeConsoleSession;
 import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.events.EventCategoryPanel;
 import org.apache.syncope.client.console.panels.search.AbstractSearchPanel;
 import org.apache.syncope.client.console.panels.search.AnyObjectSearchPanel;
 import org.apache.syncope.client.console.panels.search.GroupSearchPanel;
 import org.apache.syncope.client.console.panels.search.SearchClause;
 import org.apache.syncope.client.console.panels.search.UserSearchPanel;
 import org.apache.syncope.client.console.rest.AnyTypeRestClient;
+import org.apache.syncope.client.console.rest.ImplementationRestClient;
 import org.apache.syncope.client.console.rest.LoggerRestClient;
 import org.apache.syncope.client.console.rest.NotificationRestClient;
 import org.apache.syncope.client.console.rest.SchemaRestClient;
@@ -44,7 +43,6 @@ import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPa
 import org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.client.console.wicket.markup.html.form.MultiPanel;
 import org.apache.syncope.client.console.wizards.AjaxWizardBuilder;
-import org.apache.syncope.common.lib.info.JavaImplInfo;
 import org.apache.syncope.common.lib.to.DerSchemaTO;
 import org.apache.syncope.common.lib.to.EntityTO;
 import org.apache.syncope.common.lib.to.NotificationTO;
@@ -80,6 +78,8 @@ public class NotificationWizardBuilder extends AjaxWizardBuilder<NotificationWra
 
     private final LoggerRestClient loggerRestClient = new LoggerRestClient();
 
+    private final ImplementationRestClient implRestClient = new ImplementationRestClient();
+
     /**
      * Construct.
      *
@@ -107,10 +107,10 @@ public class NotificationWizardBuilder extends AjaxWizardBuilder<NotificationWra
 
     @Override
     protected WizardModel buildModelSteps(final NotificationWrapper modelObject, final WizardModel wizardModel) {
-        wizardModel.add(new NotificationWizardBuilder.Details(modelObject));
-        wizardModel.add(new NotificationWizardBuilder.Recipients(modelObject));
-        wizardModel.add(new NotificationWizardBuilder.Events(modelObject));
-        wizardModel.add(new NotificationWizardBuilder.Abouts(modelObject));
+        wizardModel.add(new Details(modelObject));
+        wizardModel.add(new Recipients(modelObject));
+        wizardModel.add(new Events(modelObject));
+        wizardModel.add(new Abouts(modelObject));
         return wizardModel;
     }
 
@@ -332,13 +332,8 @@ public class NotificationWizardBuilder extends AjaxWizardBuilder<NotificationWra
 
             @Override
             protected List<String> load() {
-                Optional<JavaImplInfo> providers = SyncopeConsoleSession.get().getPlatformInfo().
-                        getJavaImplInfo(ImplementationType.RECIPIENTS_PROVIDER);
-                List<String> load = providers.isPresent()
-                        ? new ArrayList<>(providers.get().getClasses())
-                        : new ArrayList<>();
-                Collections.sort(load);
-                return load;
+                return implRestClient.list(ImplementationType.RECIPIENTS_PROVIDER).stream().
+                        map(EntityTO::getKey).sorted().collect(Collectors.toList());
             }
         };
 
@@ -371,11 +366,11 @@ public class NotificationWizardBuilder extends AjaxWizardBuilder<NotificationWra
                     new PropertyModel<>(modelObject, "recipientClauses")).
                     required(false).build("recipients"));
 
-            AjaxDropDownChoicePanel<String> recipientsProviderClassName = new AjaxDropDownChoicePanel<>(
-                    "recipientsProviderClassName", "recipientsProviderClassName",
-                    new PropertyModel<>(notificationTO, "recipientsProviderClassName"), false);
-            recipientsProviderClassName.setChoices(recipientProviders.getObject());
-            add(recipientsProviderClassName);
+            AjaxDropDownChoicePanel<String> recipientsProvider = new AjaxDropDownChoicePanel<>(
+                    "recipientsProvider", "recipientsProvider",
+                    new PropertyModel<>(notificationTO, "recipientsProvider"), false);
+            recipientsProvider.setChoices(recipientProviders.getObject());
+            add(recipientsProvider);
 
             AjaxCheckBoxPanel selfAsRecipient = new AjaxCheckBoxPanel("selfAsRecipient",
                     getString("selfAsRecipient"), new PropertyModel<>(notificationTO, "selfAsRecipient"));

http://git-wip-us.apache.org/repos/asf/syncope/blob/98890722/client/console/src/main/java/org/apache/syncope/client/console/pages/Audit.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/Audit.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/Audit.java
index 33df05a..09ab57e 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/pages/Audit.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/Audit.java
@@ -18,10 +18,10 @@
  */
 package org.apache.syncope.client.console.pages;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.stream.Collectors;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
@@ -49,15 +49,14 @@ public class Audit extends BasePage {
 
         final LoggerRestClient loggerRestClient = new LoggerRestClient();
 
-        List<String> events = new ArrayList<>();
-        for (AuditLoggerName audit : loggerRestClient.listAudits()) {
-            events.add(AuditLoggerName.buildEvent(
-                    audit.getType(),
-                    audit.getCategory(),
-                    audit.getSubcategory(),
-                    audit.getEvent(),
-                    audit.getResult()));
-        }
+        List<String> events = loggerRestClient.listAudits().stream().
+                map(audit -> AuditLoggerName.buildEvent(
+                audit.getType(),
+                audit.getCategory(),
+                audit.getSubcategory(),
+                audit.getEvent(),
+                audit.getResult())).
+                collect(Collectors.toList());
 
         WebMarkupContainer content = new WebMarkupContainer("content");
         content.setOutputMarkupId(true);
@@ -89,7 +88,7 @@ public class Audit extends BasePage {
                     final SelectedEventsPanel.EventSelectionChanged eventSelectionChanged =
                             (SelectedEventsPanel.EventSelectionChanged) event.getPayload();
 
-                    for (String toBeRemoved : eventSelectionChanged.getToBeRemoved()) {
+                    eventSelectionChanged.getToBeRemoved().forEach(toBeRemoved -> {
                         Pair<EventCategoryTO, AuditElements.Result> eventCategory =
                                 AuditLoggerName.parseEventCategory(toBeRemoved);
 
@@ -102,9 +101,9 @@ public class Audit extends BasePage {
                                 eventCategory.getValue());
 
                         loggerRestClient.disableAudit(auditLoggerName);
-                    }
+                    });
 
-                    for (String toBeAdded : eventSelectionChanged.getToBeAdded()) {
+                    eventSelectionChanged.getToBeAdded().forEach(toBeAdded -> {
                         Pair<EventCategoryTO, AuditElements.Result> eventCategory =
                                 AuditLoggerName.parseEventCategory(toBeAdded);
 
@@ -117,7 +116,7 @@ public class Audit extends BasePage {
                                 eventCategory.getValue());
 
                         loggerRestClient.enableAudit(auditLoggerName);
-                    }
+                    });
                 }
             }
         });

http://git-wip-us.apache.org/repos/asf/syncope/blob/98890722/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java
index fad2ac1..0cff36c 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java
@@ -193,6 +193,12 @@ public class BasePage extends WebPage implements IAjaxIndicatorAware {
         MetaDataRoleAuthorizationStrategy.authorize(link, WebPage.RENDER, StandardEntitlement.AUDIT_LIST);
         liContainer.add(link);
 
+        liContainer = new WebMarkupContainer(getLIContainerId("implementations"));
+        confULContainer.add(liContainer);
+        link = BookmarkablePageLinkBuilder.build("implementations", Implementations.class);
+        MetaDataRoleAuthorizationStrategy.authorize(link, WebPage.RENDER, StandardEntitlement.IMPLEMENTATION_LIST);
+        liContainer.add(link);
+
         liContainer = new WebMarkupContainer(getLIContainerId("logs"));
         confULContainer.add(liContainer);
         link = BookmarkablePageLinkBuilder.build("logs", Logs.class);

http://git-wip-us.apache.org/repos/asf/syncope/blob/98890722/client/console/src/main/java/org/apache/syncope/client/console/pages/Implementations.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/Implementations.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/Implementations.java
new file mode 100644
index 0000000..0cd1c43
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/Implementations.java
@@ -0,0 +1,66 @@
+/*
+ * 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.client.console.pages;
+
+import de.agilecoders.wicket.core.markup.html.bootstrap.tabs.AjaxBootstrapTabbedPanel;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
+import org.apache.syncope.client.console.panels.ImplementationDirectoryPanel;
+import org.apache.syncope.common.lib.types.ImplementationType;
+import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
+import org.apache.wicket.extensions.markup.html.tabs.ITab;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+
+public class Implementations extends BasePage {
+
+    private static final long serialVersionUID = -4562707092152823781L;
+
+    public Implementations(final PageParameters parameters) {
+        super(parameters);
+
+        body.add(BookmarkablePageLinkBuilder.build("dashboard", "dashboardBr", Dashboard.class));
+
+        WebMarkupContainer content = new WebMarkupContainer("content");
+        content.setOutputMarkupId(true);
+        content.setMarkupId("implementations");
+        content.add(new AjaxBootstrapTabbedPanel<>("tabbedPanel", buildTabList()));
+        body.add(content);
+    }
+
+    private List<ITab> buildTabList() {
+        return Arrays.stream(ImplementationType.values()).
+                filter(type -> type != ImplementationType.JWT_SSO_PROVIDER
+                && type != ImplementationType.AUDIT_APPENDER).
+                sorted(Comparator.comparing(ImplementationType::name)).
+                map(type -> new AbstractTab(Model.of(type.name())) {
+
+            private static final long serialVersionUID = -5861786415855103549L;
+
+            @Override
+            public WebMarkupContainer getPanel(final String panelId) {
+                return new ImplementationDirectoryPanel(panelId, type, getPageReference());
+            }
+        }).collect(Collectors.toList());
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/98890722/client/console/src/main/java/org/apache/syncope/client/console/panels/ImplementationDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ImplementationDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ImplementationDirectoryPanel.java
new file mode 100644
index 0000000..60a56d1
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ImplementationDirectoryPanel.java
@@ -0,0 +1,232 @@
+/*
+ * 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.client.console.panels;
+
+import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.commons.DirectoryDataProvider;
+import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.panels.ImplementationDirectoryPanel.ImplementationProvider;
+import org.apache.syncope.client.console.rest.ImplementationRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksTogglePanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.ImplementationTO;
+import org.apache.syncope.common.lib.types.ImplementationEngine;
+import org.apache.syncope.common.lib.types.ImplementationType;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.model.AbstractReadOnlyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.StringResourceModel;
+
+public class ImplementationDirectoryPanel extends DirectoryPanel<
+        ImplementationTO, ImplementationTO, ImplementationProvider, ImplementationRestClient> {
+
+    private static final long serialVersionUID = 1868839768348072635L;
+
+    private final ImplementationType type;
+
+    public ImplementationDirectoryPanel(final String id, final ImplementationType type, final PageReference pageRef) {
+        super(id, pageRef, true);
+        this.type = type;
+
+        ImplementationTO implementation = new ImplementationTO();
+        implementation.setType(type);
+
+        disableCheckBoxes();
+
+        modal.size(Modal.Size.Large);
+        modal.addSubmitButton();
+        modal.setWindowClosedCallback(new ModalWindow.WindowClosedCallback() {
+
+            private static final long serialVersionUID = 8804221891699487139L;
+
+            @Override
+            public void onClose(final AjaxRequestTarget target) {
+                implementation.setEngine(null);
+                updateResultTable(target);
+                modal.show(false);
+            }
+        });
+        setFooterVisibility(true);
+
+        restClient = new ImplementationRestClient();
+
+        initResultTable();
+
+        ImplementationEngineTogglePanel engineTogglePanel =
+                new ImplementationEngineTogglePanel("engineTogglePanel", implementation, pageRef) {
+
+            private static final long serialVersionUID = -112426445257072782L;
+
+            @Override
+            protected void onSubmit(final ImplementationEngine engine, final AjaxRequestTarget target) {
+                implementation.setKey(null);
+                implementation.setBody(null);
+
+                target.add(ImplementationDirectoryPanel.this.modal.setContent(new ImplementationModalPanel(
+                        ImplementationDirectoryPanel.this.modal, implementation, pageRef)));
+                ImplementationDirectoryPanel.this.modal.header(
+                        new StringResourceModel("any.new", Model.of(implementation)));
+                ImplementationDirectoryPanel.this.modal.show(true);
+            }
+        };
+        addInnerObject(engineTogglePanel);
+
+        AjaxLink<Void> replaceAddLink = new AjaxLink<Void>("add") {
+
+            private static final long serialVersionUID = -7978723352517770644L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+                send(ImplementationDirectoryPanel.this, Broadcast.BREADTH,
+                        new ActionLinksTogglePanel.ActionLinkToggleCloseEventPayload(target));
+                engineTogglePanel.setHeaderLabel(target);
+                engineTogglePanel.toggle(target, true);
+            }
+        };
+        ((WebMarkupContainer) get("container:content")).addOrReplace(replaceAddLink);
+
+        MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, RENDER, StandardEntitlement.IMPLEMENTATION_CREATE);
+    }
+
+    @Override
+    protected List<IColumn<ImplementationTO, String>> getColumns() {
+        List<IColumn<ImplementationTO, String>> columns = new ArrayList<>();
+
+        columns.add(new PropertyColumn<>(new StringResourceModel("key", this), "key", "key"));
+        columns.add(new PropertyColumn<>(new StringResourceModel("engine", this), "engine", "engine"));
+
+        return columns;
+    }
+
+    @Override
+    protected ActionsPanel<ImplementationTO> getActions(final IModel<ImplementationTO> model) {
+        final ActionsPanel<ImplementationTO> panel = super.getActions(model);
+
+        panel.add(new ActionLink<ImplementationTO>() {
+
+            private static final long serialVersionUID = -7978723352517770645L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final ImplementationTO ignore) {
+                target.add(modal.setContent(
+                        new ImplementationModalPanel(modal, model.getObject(), pageRef)));
+                modal.header(new StringResourceModel("any.edit", Model.of(model.getObject())));
+                modal.show(true);
+            }
+        }, ActionLink.ActionType.EDIT, StandardEntitlement.IMPLEMENTATION_UPDATE);
+
+        panel.add(new ActionLink<ImplementationTO>() {
+
+            private static final long serialVersionUID = -3722207913631435501L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final ImplementationTO ignore) {
+                try {
+                    restClient.delete(model.getObject().getKey());
+                    SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
+                    target.add(container);
+                } catch (SyncopeClientException e) {
+                    LOG.error("While deleting object {}", model.getObject().getKey(), e);
+                    SyncopeConsoleSession.get().error(
+                            StringUtils.isBlank(e.getMessage()) ? e.getClass().getName() : e.getMessage());
+                }
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        }, ActionLink.ActionType.DELETE, StandardEntitlement.IMPLEMENTATION_DELETE, true);
+
+        return panel;
+    }
+
+    @Override
+    protected ImplementationProvider dataProvider() {
+        return new ImplementationProvider(rows);
+    }
+
+    @Override
+    protected String paginatorRowsKey() {
+        return Constants.PREF_IMPLEMENTATION_PAGINATOR_ROWS;
+    }
+
+    @Override
+    protected Collection<ActionLink.ActionType> getBulkActions() {
+        return Collections.<ActionLink.ActionType>emptyList();
+    }
+
+    protected class ImplementationProvider extends DirectoryDataProvider<ImplementationTO> {
+
+        private static final long serialVersionUID = 8594921866993979224L;
+
+        private final SortableDataProviderComparator<ImplementationTO> comparator;
+
+        public ImplementationProvider(final int paginatorRows) {
+            super(paginatorRows);
+
+            setSort("key", SortOrder.ASCENDING);
+            comparator = new SortableDataProviderComparator<>(this);
+        }
+
+        @Override
+        public Iterator<ImplementationTO> iterator(final long first, final long count) {
+            List<ImplementationTO> list = restClient.list(type);
+            Collections.sort(list, comparator);
+            return list.subList((int) first, (int) first + (int) count).iterator();
+        }
+
+        @Override
+        public long size() {
+            return restClient.list(type).size();
+        }
+
+        @Override
+        public IModel<ImplementationTO> model(final ImplementationTO implementation) {
+            return new AbstractReadOnlyModel<ImplementationTO>() {
+
+                private static final long serialVersionUID = 999513782683391483L;
+
+                @Override
+                public ImplementationTO getObject() {
+                    return implementation;
+                }
+            };
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/98890722/client/console/src/main/java/org/apache/syncope/client/console/panels/ImplementationEngineTogglePanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ImplementationEngineTogglePanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ImplementationEngineTogglePanel.java
new file mode 100644
index 0000000..49819d5
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ImplementationEngineTogglePanel.java
@@ -0,0 +1,84 @@
+/*
+ * 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.client.console.panels;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
+import org.apache.syncope.common.lib.to.ImplementationTO;
+import org.apache.syncope.common.lib.types.ImplementationEngine;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.model.PropertyModel;
+
+public abstract class ImplementationEngineTogglePanel extends TogglePanel<Serializable> {
+
+    private static final long serialVersionUID = -7869528596656778267L;
+
+    public ImplementationEngineTogglePanel(
+            final String id,
+            final ImplementationTO implementation,
+            final PageReference pageRef) {
+
+        super(id, pageRef);
+
+        Form<?> form = new Form<>("implementationEngineForm");
+        addInnerObject(form);
+
+        PropertyModel<ImplementationEngine> engineModel = new PropertyModel<>(implementation, "engine");
+
+        form.add(new AjaxDropDownChoicePanel<>(
+                "engine", "Engine", engineModel, false).
+                setNullValid(false).
+                setChoices(Arrays.stream(ImplementationEngine.values()).collect(Collectors.toList())).
+                setStyleSheet("form-control").
+                hideLabel());
+
+        form.add(new AjaxSubmitLink("changeit", form) {
+
+            private static final long serialVersionUID = -7978723352517770644L;
+
+            @Override
+            protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+                ImplementationEngineTogglePanel.this.onSubmit(engineModel.getObject(), target);
+                target.add(form);
+                toggle(target, false);
+
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+
+            @Override
+            protected void onError(final AjaxRequestTarget target, final Form<?> form) {
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        });
+
+    }
+
+    protected abstract void onSubmit(ImplementationEngine engine, AjaxRequestTarget target);
+
+    public void setHeaderLabel(final AjaxRequestTarget target) {
+        setHeader(target, getString("engine"));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/98890722/client/console/src/main/java/org/apache/syncope/client/console/panels/ImplementationModalPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ImplementationModalPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ImplementationModalPanel.java
new file mode 100644
index 0000000..a2d4690
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ImplementationModalPanel.java
@@ -0,0 +1,329 @@
+/*
+ * 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.client.console.panels;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.SyncopeConsoleApplication;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.init.ClassPathScanImplementationLookup;
+import org.apache.syncope.client.console.init.ConsoleInitializer;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.rest.ImplementationRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.common.lib.info.JavaImplInfo;
+import org.apache.syncope.common.lib.to.ImplementationTO;
+import org.apache.syncope.common.lib.types.ImplementationEngine;
+import org.apache.syncope.common.lib.types.ImplementationType;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxEventBehavior;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.OnLoadHeaderItem;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.TextArea;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.util.io.IOUtils;
+
+public class ImplementationModalPanel extends AbstractModalPanel<ImplementationTO> {
+
+    private static final long serialVersionUID = 5283548960927517342L;
+
+    private enum ViewMode {
+        JAVA_CLASS,
+        JSON_BODY,
+        GROOVY_BODY
+
+    }
+
+    private static final ObjectMapper MAPPER = new ObjectMapper();
+
+    private final ImplementationRestClient restClient = new ImplementationRestClient();
+
+    private final ImplementationTO implementation;
+
+    private final ViewMode viewMode;
+
+    private boolean create = false;
+
+    public ImplementationModalPanel(
+            final BaseModal<ImplementationTO> modal,
+            final ImplementationTO implementation,
+            final PageReference pageRef) {
+
+        super(modal, pageRef);
+        this.implementation = implementation;
+        this.viewMode = implementation.getEngine() == ImplementationEngine.GROOVY
+                ? ViewMode.GROOVY_BODY
+                : implementation.getType() == ImplementationType.REPORTLET
+                || implementation.getType() == ImplementationType.ACCOUNT_RULE
+                || implementation.getType() == ImplementationType.PASSWORD_RULE
+                ? ViewMode.JSON_BODY
+                : ViewMode.JAVA_CLASS;
+        this.create = implementation.getKey() == null;
+
+        add(new AjaxTextFieldPanel(
+                "key", "key", new PropertyModel<>(implementation, "key"), false).
+                addRequiredLabel().setEnabled(create));
+
+        List<String> classes = Collections.emptyList();
+        if (viewMode == ViewMode.JAVA_CLASS) {
+            Optional<JavaImplInfo> javaClasses = SyncopeConsoleSession.get().getPlatformInfo().
+                    getJavaImplInfo(implementation.getType());
+            classes = javaClasses.isPresent()
+                    ? new ArrayList<>(javaClasses.get().getClasses())
+                    : new ArrayList<>();
+        } else if (viewMode == ViewMode.JSON_BODY) {
+            ClassPathScanImplementationLookup implementationLookup =
+                    (ClassPathScanImplementationLookup) SyncopeConsoleApplication.get().
+                            getServletContext().getAttribute(ConsoleInitializer.CLASSPATH_LOOKUP);
+
+            switch (implementation.getType()) {
+                case REPORTLET:
+                    classes = implementationLookup.getReportletConfs().keySet().stream().
+                            collect(Collectors.toList());
+                    break;
+
+                case ACCOUNT_RULE:
+                    classes = implementationLookup.getAccountRuleConfs().keySet().stream().
+                            collect(Collectors.toList());
+                    break;
+
+                case PASSWORD_RULE:
+                    classes = implementationLookup.getPasswordRuleConfs().keySet().stream().
+                            collect(Collectors.toList());
+                    break;
+
+                default:
+            }
+        }
+        Collections.sort(classes);
+
+        AjaxDropDownChoicePanel<String> javaClass = new AjaxDropDownChoicePanel<>(
+                "javaClass", "Class", new PropertyModel<>(implementation, "body"));
+        javaClass.setNullValid(false);
+        javaClass.setChoices(classes);
+        javaClass.addRequiredLabel();
+        javaClass.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true);
+        javaClass.setVisible(viewMode == ViewMode.JAVA_CLASS);
+        add(javaClass);
+
+        AjaxDropDownChoicePanel<String> jsonClass = new AjaxDropDownChoicePanel<>(
+                "jsonClass", "Class", new Model<>());
+        jsonClass.setNullValid(false);
+        jsonClass.setChoices(classes);
+        jsonClass.addRequiredLabel();
+        jsonClass.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true);
+        jsonClass.setVisible(viewMode == ViewMode.JSON_BODY);
+        if (viewMode == ViewMode.JSON_BODY && StringUtils.isNotBlank(implementation.getBody())) {
+            try {
+                JsonNode node = MAPPER.readTree(implementation.getBody());
+                if (node.has("@class")) {
+                    jsonClass.setModelObject(node.get("@class").asText());
+                }
+            } catch (IOException e) {
+                LOG.error("Could not parse as JSON payload: {}", implementation.getBody(), e);
+            }
+        }
+        jsonClass.setReadOnly(jsonClass.getModelObject() != null);
+        add(jsonClass);
+
+        WebMarkupContainer groovyClassContainer = new WebMarkupContainer("groovyClassContainer");
+        groovyClassContainer.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true);
+        groovyClassContainer.setVisible(viewMode != ViewMode.JAVA_CLASS);
+        add(groovyClassContainer);
+
+        if (StringUtils.isBlank(implementation.getBody())
+                && implementation.getEngine() == ImplementationEngine.GROOVY) {
+
+            String templateClassName = null;
+
+            switch (implementation.getType()) {
+                case REPORTLET:
+                    templateClassName = "MyReportlet";
+                    break;
+
+                case ACCOUNT_RULE:
+                    templateClassName = "MyAccountRule";
+                    break;
+
+                case PASSWORD_RULE:
+                    templateClassName = "MyPasswordRule";
+                    break;
+
+                case ITEM_TRANSFORMER:
+                    templateClassName = "MyItemTransformer";
+                    break;
+
+                case TASKJOB_DELEGATE:
+                    templateClassName = "MySchedTaskJobDelegate";
+                    break;
+
+                case RECON_FILTER_BUILDER:
+                    templateClassName = "MyReconFilterBuilder";
+                    break;
+
+                case LOGIC_ACTIONS:
+                    templateClassName = "MyLogicActions";
+                    break;
+
+                case PROPAGATION_ACTIONS:
+                    templateClassName = "MyPropagationActions";
+                    break;
+
+                case PULL_ACTIONS:
+                    templateClassName = "MyPullActions";
+                    break;
+
+                case PUSH_ACTIONS:
+                    templateClassName = "MyPushActions";
+                    break;
+
+                case PULL_CORRELATION_RULE:
+                    templateClassName = "MyPullCorrelationRule";
+                    break;
+
+                case VALIDATOR:
+                    templateClassName = "MyValidator";
+                    break;
+
+                case RECIPIENTS_PROVIDER:
+                    templateClassName = "MyRecipientsProvider";
+                    break;
+
+                default:
+            }
+
+            if (templateClassName != null) {
+                try {
+                    implementation.setBody(StringUtils.substringAfter(IOUtils.toString(getClass().
+                            getResourceAsStream(
+                                    "/org/apache/syncope/client/console/implementations/" + templateClassName
+                                    + ".groovy")),
+                            "*/\n"));
+                } catch (IOException e) {
+                    LOG.error("Could not load the expected Groovy template {} for {}",
+                            templateClassName, implementation.getType(), e);
+                }
+            }
+        }
+
+        TextArea<String> groovyClass = new TextArea<>("groovyClass", new PropertyModel<>(implementation, "body"));
+        groovyClass.setMarkupId("groovyClass").setOutputMarkupPlaceholderTag(true);
+        groovyClass.setVisible(viewMode != ViewMode.JAVA_CLASS);
+        groovyClass.setRequired(true);
+        groovyClassContainer.add(groovyClass);
+
+        jsonClass.add(new AjaxEventBehavior(Constants.ON_CHANGE) {
+
+            private static final long serialVersionUID = 5538299138211283825L;
+
+            @Override
+            protected void onEvent(final AjaxRequestTarget target) {
+                ClassPathScanImplementationLookup implementationLookup =
+                        (ClassPathScanImplementationLookup) SyncopeConsoleApplication.get().
+                                getServletContext().getAttribute(ConsoleInitializer.CLASSPATH_LOOKUP);
+
+                Class<?> clazz = null;
+                switch (implementation.getType()) {
+                    case REPORTLET:
+                        clazz = implementationLookup.getReportletConfs().get(jsonClass.getModelObject());
+                        break;
+
+                    case ACCOUNT_RULE:
+                        clazz = implementationLookup.getAccountRuleConfs().get(jsonClass.getModelObject());
+                        break;
+
+                    case PASSWORD_RULE:
+                        clazz = implementationLookup.getPasswordRuleConfs().get(jsonClass.getModelObject());
+                        break;
+
+                    default:
+                }
+
+                if (clazz != null) {
+                    try {
+                        target.appendJavaScript("editor.getDoc().setValue('"
+                                + MAPPER.writeValueAsString(clazz.newInstance())
+                                + "');");
+                    } catch (Exception e) {
+                        LOG.error("Could not generate a value for {}", jsonClass.getModelObject(), e);
+                    }
+                }
+            }
+        });
+    }
+
+    @Override
+    public ImplementationTO getItem() {
+        return implementation;
+    }
+
+    @Override
+    public void renderHead(final IHeaderResponse response) {
+        super.renderHead(response);
+        if (viewMode != ViewMode.JAVA_CLASS) {
+            response.render(OnLoadHeaderItem.forScript(
+                    "editor = CodeMirror.fromTextArea("
+                    + "document.getElementById('groovyClassForm').children['groovyClass'], {"
+                    + "  readOnly: false, "
+                    + "  lineNumbers: true, "
+                    + "  lineWrapping: true, "
+                    + "  matchBrackets: true,"
+                    + "  autoCloseBrackets: true,"
+                    + (viewMode == ViewMode.GROOVY_BODY ? "  mode: 'text/x-groovy'," : "")
+                    + "  autoRefresh: true"
+                    + "});"
+                    + "editor.on('change', updateTextArea);"));
+        }
+    }
+
+    @Override
+    public void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+        try {
+            if (create) {
+                restClient.create(implementation);
+            } else {
+                restClient.update(implementation);
+            }
+
+            modal.close(target);
+            SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
+        } catch (Exception e) {
+            LOG.error("While creating or updating AttrTO", e);
+            SyncopeConsoleSession.get().error(StringUtils.isBlank(e.getMessage())
+                    ? e.getClass().getName()
+                    : e.getMessage());
+        }
+        ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/98890722/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersEditModalPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersEditModalPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersEditModalPanel.java
index 85a04b4..e85a39e 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersEditModalPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersEditModalPanel.java
@@ -35,9 +35,7 @@ public class ParametersEditModalPanel extends AbstractModalPanel<AttrTO> {
 
     private final AttrTO attrTO;
 
-    private final BaseModal<AttrTO> parametersModal;
-
-    private final ConfRestClient confRestClient = new ConfRestClient();
+    private final ConfRestClient restClient = new ConfRestClient();
 
     public ParametersEditModalPanel(
             final BaseModal<AttrTO> modal,
@@ -46,7 +44,6 @@ public class ParametersEditModalPanel extends AbstractModalPanel<AttrTO> {
 
         super(modal, pageRef);
         this.attrTO = attrTO;
-        this.parametersModal = modal;
         add(new ParametersDetailsPanel("parametersDetailsPanel", getItem()));
     }
 
@@ -58,8 +55,8 @@ public class ParametersEditModalPanel extends AbstractModalPanel<AttrTO> {
     @Override
     public void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
         try {
-            confRestClient.set(attrTO);
-            parametersModal.close(target);
+            restClient.set(attrTO);
+            modal.close(target);
             SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
         } catch (Exception e) {
             LOG.error("While creating or updating AttrTO", e);

http://git-wip-us.apache.org/repos/asf/syncope/blob/98890722/client/console/src/main/java/org/apache/syncope/client/console/panels/PlainSchemaDetails.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/PlainSchemaDetails.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/PlainSchemaDetails.java
index ceff741..e79c013 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/PlainSchemaDetails.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/PlainSchemaDetails.java
@@ -24,21 +24,21 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Optional;
+import java.util.stream.Collectors;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.SyncopeConsoleApplication;
-import org.apache.syncope.client.console.SyncopeConsoleSession;
 import org.apache.syncope.client.console.commons.Constants;
 import org.apache.syncope.client.console.commons.PropertyList;
 import org.apache.syncope.client.console.init.ConsoleInitializer;
 import org.apache.syncope.client.console.init.MIMETypesLoader;
+import org.apache.syncope.client.console.rest.ImplementationRestClient;
 import org.apache.syncope.client.console.wicket.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
 import org.apache.syncope.client.console.wicket.markup.html.form.AjaxCheckBoxPanel;
 import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
 import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
 import org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
-import org.apache.syncope.common.lib.info.JavaImplInfo;
 import org.apache.syncope.common.lib.to.AbstractSchemaTO;
+import org.apache.syncope.common.lib.to.EntityTO;
 import org.apache.syncope.common.lib.to.PlainSchemaTO;
 import org.apache.syncope.common.lib.types.AttrSchemaType;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
@@ -61,6 +61,8 @@ public class PlainSchemaDetails extends AbstractSchemaDetailsPanel {
     private static final MIMETypesLoader MIME_TYPES_LOADER = (MIMETypesLoader) SyncopeConsoleApplication.get().
             getServletContext().getAttribute(ConsoleInitializer.MIMETYPES_LOADER);
 
+    private final ImplementationRestClient implRestClient = new ImplementationRestClient();
+
     private final MultiFieldPanel<String> enumerationValues;
 
     private final MultiFieldPanel<String> enumerationKeys;
@@ -235,26 +237,21 @@ public class PlainSchemaDetails extends AbstractSchemaDetailsPanel {
         }
         );
 
-        IModel<List<String>> validatorsList = new LoadableDetachableModel<List<String>>() {
+        IModel<List<String>> validators = new LoadableDetachableModel<List<String>>() {
 
             private static final long serialVersionUID = 5275935387613157437L;
 
             @Override
             protected List<String> load() {
-                Optional<JavaImplInfo> validators = SyncopeConsoleSession.get().getPlatformInfo().
-                        getJavaImplInfo(ImplementationType.VALIDATOR);
-                List<String> load = validators.isPresent()
-                        ? new ArrayList<>(validators.get().getClasses())
-                        : new ArrayList<>();
-                Collections.sort(load);
-                return load;
+                return implRestClient.list(ImplementationType.VALIDATOR).stream().
+                        map(EntityTO::getKey).sorted().collect(Collectors.toList());
             }
         };
-        final AjaxDropDownChoicePanel<String> validatorClass = new AjaxDropDownChoicePanel<>("validatorClass",
-                getString("validatorClass"), new PropertyModel<>(schemaTO, "validatorClass"));
-        ((DropDownChoice) validatorClass.getField()).setNullValid(true);
-        validatorClass.setChoices(validatorsList.getObject());
-        schemaForm.add(validatorClass);
+        final AjaxDropDownChoicePanel<String> validator = new AjaxDropDownChoicePanel<>("validator",
+                getString("validator"), new PropertyModel<>(schemaTO, "validator"));
+        ((DropDownChoice) validator.getField()).setNullValid(true);
+        validator.setChoices(validators.getObject());
+        schemaForm.add(validator);
 
         AutoCompleteTextField<String> mandatoryCondition = new AutoCompleteTextField<String>("mandatoryCondition") {
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/98890722/client/console/src/main/java/org/apache/syncope/client/console/panels/RealmDetails.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/RealmDetails.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/RealmDetails.java
index 0c7c3dc..763422e 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/RealmDetails.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/RealmDetails.java
@@ -19,12 +19,10 @@
 package org.apache.syncope.client.console.panels;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import java.util.stream.Collectors;
-import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.rest.ImplementationRestClient;
 import org.apache.syncope.client.console.rest.PolicyRestClient;
 import org.apache.syncope.client.console.rest.ResourceRestClient;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
@@ -34,7 +32,6 @@ import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPa
 import org.apache.syncope.client.console.wicket.markup.html.form.FieldPanel;
 import org.apache.syncope.client.console.wicket.markup.html.form.PolicyRenderer;
 import org.apache.syncope.common.lib.SyncopeConstants;
-import org.apache.syncope.common.lib.info.JavaImplInfo;
 import org.apache.syncope.common.lib.to.EntityTO;
 import org.apache.syncope.common.lib.to.RealmTO;
 import org.apache.syncope.common.lib.types.ImplementationType;
@@ -59,6 +56,8 @@ public class RealmDetails extends Panel {
 
     private final PolicyRestClient policyRestClient = new PolicyRestClient();
 
+    private final ImplementationRestClient implRestClient = new ImplementationRestClient();
+
     private final IModel<Map<String, String>> accountPolicies = new LoadableDetachableModel<Map<String, String>>() {
 
         private static final long serialVersionUID = -2012833443695917883L;
@@ -81,19 +80,14 @@ public class RealmDetails extends Panel {
         }
     };
 
-    private final IModel<List<String>> logicActionsClasses = new LoadableDetachableModel<List<String>>() {
+    private final IModel<List<String>> logicActions = new LoadableDetachableModel<List<String>>() {
 
         private static final long serialVersionUID = 5275935387613157437L;
 
         @Override
         protected List<String> load() {
-            Optional<JavaImplInfo> actions = SyncopeConsoleSession.get().getPlatformInfo().
-                    getJavaImplInfo(ImplementationType.LOGIC_ACTIONS);
-            List<String> load = actions.isPresent()
-                    ? new ArrayList<>(actions.get().getClasses())
-                    : new ArrayList<>();
-            Collections.sort(load);
-            return load;
+            return implRestClient.list(ImplementationType.LOGIC_ACTIONS).stream().
+                    map(EntityTO::getKey).sorted().collect(Collectors.toList());
         }
     };
 
@@ -106,7 +100,7 @@ public class RealmDetails extends Panel {
     public RealmDetails(
             final String id,
             final RealmTO realmTO,
-            final ActionsPanel<?> actions,
+            final ActionsPanel<?> actionsPanel,
             final boolean unwrapped) {
 
         super(id);
@@ -149,13 +143,13 @@ public class RealmDetails extends Panel {
         ((DropDownChoice<?>) passwordPolicy.getField()).setNullValid(true);
         container.add(passwordPolicy);
 
-        AjaxPalettePanel<String> actionsClassNames = new AjaxPalettePanel.Builder<String>().
+        AjaxPalettePanel<String> actions = new AjaxPalettePanel.Builder<String>().
                 setAllowMoveAll(true).setAllowOrder(true).
-                build("actionsClassNames",
-                        new PropertyModel<List<String>>(realmTO, "actionsClassNames"),
-                        new ListModel<>(logicActionsClasses.getObject()));
-        actionsClassNames.setOutputMarkupId(true);
-        container.add(actionsClassNames);
+                build("actions",
+                        new PropertyModel<List<String>>(realmTO, "actions"),
+                        new ListModel<>(logicActions.getObject()));
+        actions.setOutputMarkupId(true);
+        container.add(actions);
 
         container.add(new AjaxPalettePanel.Builder<>().build("resources",
                 new PropertyModel<>(realmTO, "resources"),
@@ -165,11 +159,11 @@ public class RealmDetails extends Panel {
                 setEnabled(!SyncopeConstants.ROOT_REALM.equals(realmTO.getName())).
                 setVisible(!SyncopeConstants.ROOT_REALM.equals(realmTO.getName())));
 
-        if (actions == null) {
+        if (actionsPanel == null) {
             add(new Fragment("actions", "emptyFragment", this).setRenderBodyOnly(true));
         } else {
             Fragment fragment = new Fragment("actions", "actionsFragment", this);
-            fragment.add(actions);
+            fragment.add(actionsPanel);
             add(fragment.setRenderBodyOnly(true));
         }
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/98890722/client/console/src/main/java/org/apache/syncope/client/console/policies/AccountPolicyDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/policies/AccountPolicyDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/policies/AccountPolicyDirectoryPanel.java
index a2535d9..dd89ea1 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/policies/AccountPolicyDirectoryPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/policies/AccountPolicyDirectoryPanel.java
@@ -75,7 +75,7 @@ public class AccountPolicyDirectoryPanel extends PolicyDirectoryPanel<AccountPol
                         ruleCompositionModal, model.getObject().getKey(), PolicyType.ACCOUNT, pageRef)));
 
                 ruleCompositionModal.header(new StringResourceModel(
-                        "policy.rule.conf", AccountPolicyDirectoryPanel.this, Model.of(model.getObject())));
+                        "policy.rules", AccountPolicyDirectoryPanel.this, Model.of(model.getObject())));
 
                 MetaDataRoleAuthorizationStrategy.authorize(
                         ruleCompositionModal.getForm(), ENABLE, StandardEntitlement.POLICY_UPDATE);

http://git-wip-us.apache.org/repos/asf/syncope/blob/98890722/client/console/src/main/java/org/apache/syncope/client/console/policies/PasswordPolicyDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/policies/PasswordPolicyDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/policies/PasswordPolicyDirectoryPanel.java
index 8e3663d..32fce97 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/policies/PasswordPolicyDirectoryPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/policies/PasswordPolicyDirectoryPanel.java
@@ -72,7 +72,7 @@ public class PasswordPolicyDirectoryPanel extends PolicyDirectoryPanel<PasswordP
                         ruleCompositionModal, model.getObject().getKey(), PolicyType.PASSWORD, pageRef)));
 
                 ruleCompositionModal.header(new StringResourceModel(
-                        "policy.rule.conf", PasswordPolicyDirectoryPanel.this, Model.of(model.getObject())));
+                        "policy.rules", PasswordPolicyDirectoryPanel.this, Model.of(model.getObject())));
 
                 MetaDataRoleAuthorizationStrategy.authorize(
                         ruleCompositionModal.getForm(), ENABLE, StandardEntitlement.POLICY_UPDATE);

http://git-wip-us.apache.org/repos/asf/syncope/blob/98890722/client/console/src/main/java/org/apache/syncope/client/console/policies/PolicyRuleDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/policies/PolicyRuleDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/policies/PolicyRuleDirectoryPanel.java
index 8b06630..2b5f8d4 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/policies/PolicyRuleDirectoryPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/policies/PolicyRuleDirectoryPanel.java
@@ -18,12 +18,14 @@
  */
 package org.apache.syncope.client.console.policies;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.stream.Collectors;
 import org.apache.commons.lang3.SerializationUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
@@ -33,6 +35,7 @@ import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
 import org.apache.syncope.client.console.pages.BasePage;
 import org.apache.syncope.client.console.panels.DirectoryPanel;
 import org.apache.syncope.client.console.panels.ModalPanel;
+import org.apache.syncope.client.console.rest.ImplementationRestClient;
 import org.apache.syncope.client.console.rest.PolicyRestClient;
 import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
@@ -44,6 +47,8 @@ import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.policy.AbstractPolicyTO;
 import org.apache.syncope.common.lib.policy.ComposablePolicy;
 import org.apache.syncope.common.lib.policy.RuleConf;
+import org.apache.syncope.common.lib.to.ImplementationTO;
+import org.apache.syncope.common.lib.types.ImplementationEngine;
 import org.apache.syncope.common.lib.types.PolicyType;
 import org.apache.wicket.PageReference;
 import org.apache.wicket.ajax.AjaxRequestTarget;
@@ -72,6 +77,8 @@ public class PolicyRuleDirectoryPanel<T extends AbstractPolicyTO> extends Direct
 
     private static final long serialVersionUID = 4984337552918213290L;
 
+    private static final ObjectMapper MAPPER = new ObjectMapper();
+
     private final BaseModal<T> baseModal;
 
     private final String policy;
@@ -88,7 +95,8 @@ public class PolicyRuleDirectoryPanel<T extends AbstractPolicyTO> extends Direct
 
         enableExitButton();
 
-        this.addNewItemPanelBuilder(new PolicyRuleWizardBuilder(policy, type, new PolicyRuleWrapper(), pageRef), true);
+        this.addNewItemPanelBuilder(
+                new PolicyRuleWizardBuilder(policy, type, new PolicyRuleWrapper(true), pageRef), true);
 
         MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, RENDER, StandardEntitlement.POLICY_UPDATE);
         initResultTable();
@@ -99,7 +107,7 @@ public class PolicyRuleDirectoryPanel<T extends AbstractPolicyTO> extends Direct
         final List<IColumn<PolicyRuleWrapper, String>> columns = new ArrayList<>();
 
         columns.add(new PropertyColumn<>(
-                new StringResourceModel("ruleConf", this), "name", "name"));
+                new StringResourceModel("rule", this), "implementationKey", "implementationKey"));
 
         columns.add(new AbstractColumn<PolicyRuleWrapper, String>(
                 new StringResourceModel("configuration", this)) {
@@ -111,7 +119,12 @@ public class PolicyRuleDirectoryPanel<T extends AbstractPolicyTO> extends Direct
                     final Item<ICellPopulator<PolicyRuleWrapper>> cellItem,
                     final String componentId,
                     final IModel<PolicyRuleWrapper> rowModel) {
-                cellItem.add(new Label(componentId, rowModel.getObject().getConf().getClass().getName()));
+
+                if (rowModel.getObject().getConf() == null) {
+                    cellItem.add(new Label(componentId, ""));
+                } else {
+                    cellItem.add(new Label(componentId, rowModel.getObject().getConf().getClass().getName()));
+                }
             }
         });
         return columns;
@@ -131,9 +144,7 @@ public class PolicyRuleDirectoryPanel<T extends AbstractPolicyTO> extends Direct
 
                 PolicyRuleDirectoryPanel.this.getTogglePanel().close(target);
                 send(PolicyRuleDirectoryPanel.this, Broadcast.EXACT,
-                        new AjaxWizard.EditItemActionEvent<>(
-                                new PolicyRuleWrapper().setConf(clone).setName(null),
-                                target));
+                        new AjaxWizard.EditItemActionEvent<>(new PolicyRuleWrapper(true).setConf(clone), target));
             }
         }, ActionLink.ActionType.CLONE, StandardEntitlement.POLICY_CREATE);
         panel.add(new ActionLink<PolicyRuleWrapper>() {
@@ -143,8 +154,13 @@ public class PolicyRuleDirectoryPanel<T extends AbstractPolicyTO> extends Direct
             @Override
             public void onClick(final AjaxRequestTarget target, final PolicyRuleWrapper ignore) {
                 PolicyRuleDirectoryPanel.this.getTogglePanel().close(target);
-                send(PolicyRuleDirectoryPanel.this, Broadcast.EXACT,
-                        new AjaxWizard.EditItemActionEvent<>(model.getObject(), target));
+                if (model.getObject().getConf() == null) {
+                    SyncopeConsoleSession.get().info(getString("noConf"));
+                    ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+                } else {
+                    send(PolicyRuleDirectoryPanel.this, Broadcast.EXACT,
+                            new AjaxWizard.EditItemActionEvent<>(model.getObject(), target));
+                }
             }
         }, ActionLink.ActionType.EDIT, StandardEntitlement.POLICY_UPDATE);
         panel.add(new ActionLink<PolicyRuleWrapper>() {
@@ -214,21 +230,46 @@ public class PolicyRuleDirectoryPanel<T extends AbstractPolicyTO> extends Direct
 
         private static final long serialVersionUID = 4725679400450513556L;
 
+        private final ImplementationRestClient implementationClient = new ImplementationRestClient();
+
         private final SortableDataProviderComparator<PolicyRuleWrapper> comparator;
 
         public PolicyRuleDataProvider(final int paginatorRows) {
             super(paginatorRows);
 
             // Default sorting
-            setSort("name", SortOrder.ASCENDING);
+            setSort("implementationKey", SortOrder.ASCENDING);
             comparator = new SortableDataProviderComparator<>(this);
         }
 
+        @SuppressWarnings("unchecked")
+        private List<PolicyRuleWrapper> getPolicyRuleWrappers(final ComposablePolicy policy) {
+            return policy.getRules().stream().map(rule -> {
+                ImplementationTO implementation = implementationClient.read(rule);
+
+                PolicyRuleWrapper wrapper = new PolicyRuleWrapper(false).
+                        setImplementationKey(implementation.getKey()).
+                        setImplementationEngine(implementation.getEngine());
+                if (implementation.getEngine() == ImplementationEngine.JAVA) {
+                    try {
+                        RuleConf ruleConf = MAPPER.readValue(implementation.getBody(), RuleConf.class);
+                        wrapper.setConf(ruleConf);
+                    } catch (Exception e) {
+                        LOG.error("During deserialization", e);
+                    }
+                }
+
+                return wrapper;
+            }).collect(Collectors.toList());
+        }
+
         @Override
         public Iterator<PolicyRuleWrapper> iterator(final long first, final long count) {
             final T actual = restClient.getPolicy(policy);
 
-            List<PolicyRuleWrapper> rules = PolicyRuleWizardBuilder.getPolicyRuleWrappers(actual);
+            List<PolicyRuleWrapper> rules = actual instanceof ComposablePolicy
+                    ? getPolicyRuleWrappers((ComposablePolicy) actual)
+                    : Collections.emptyList();
 
             Collections.sort(rules, comparator);
             return rules.subList((int) first, (int) (first + count)).iterator();
@@ -237,7 +278,9 @@ public class PolicyRuleDirectoryPanel<T extends AbstractPolicyTO> extends Direct
         @Override
         public long size() {
             final T actual = restClient.getPolicy(policy);
-            return PolicyRuleWizardBuilder.getPolicyRuleWrappers(actual).size();
+            return actual instanceof ComposablePolicy
+                    ? getPolicyRuleWrappers((ComposablePolicy) actual).size()
+                    : 0;
         }
 
         @Override