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 2015/02/06 16:52:27 UTC

[1/2] syncope git commit: [SYNCOPE-620] Extensions are now handled by console, ad Camel is the first sample

Repository: syncope
Updated Branches:
  refs/heads/2_0_X 20d29ba99 -> ba18d4055


http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/init/ImplementationClassNamesLoader.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/init/ImplementationClassNamesLoader.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/init/ImplementationClassNamesLoader.java
index 2a5098c..388beab 100644
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/init/ImplementationClassNamesLoader.java
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/init/ImplementationClassNamesLoader.java
@@ -39,7 +39,6 @@ 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.classreading.CachingMetadataReaderFactory;
 import org.springframework.core.type.filter.AssignableTypeFilter;
 import org.springframework.stereotype.Component;
 import org.springframework.util.ClassUtils;
@@ -77,8 +76,6 @@ public class ImplementationClassNamesLoader implements SyncopeLoader {
 
     @Override
     public void load() {
-        CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
-
         classNames = new EnumMap<>(Type.class);
         for (Type type : Type.values()) {
             classNames.put(type, new HashSet<String>());
@@ -128,7 +125,6 @@ public class ImplementationClassNamesLoader implements SyncopeLoader {
                 /* 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());
                 }


[2/2] syncope git commit: [SYNCOPE-620] Extensions are now handled by console, ad Camel is the first sample

Posted by il...@apache.org.
[SYNCOPE-620] Extensions are now handled by console, ad Camel is the first sample


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

Branch: refs/heads/2_0_X
Commit: ba18d4055a472a30647a47d65939c30de6d8294e
Parents: 20d29ba
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Fri Feb 6 16:52:13 2015 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Fri Feb 6 16:52:13 2015 +0100

----------------------------------------------------------------------
 .../syncope/client/console/BinaryPreview.java   |  34 ++++
 .../syncope/client/console/ExtensionPanel.java  |  34 ++++
 .../client/console/SyncopeApplication.java      |  33 ++--
 .../client/console/commons/PreviewUtil.java     |  62 +++++++
 .../console/init/BinaryPreviewersLoader.java    |  71 --------
 .../init/ImplementationClassNamesLoader.java    | 109 +++++++++++
 .../client/console/pages/Configuration.java     |  79 +++++++-
 .../syncope/client/console/pages/Schema.java    |   4 +-
 .../console/panels/AbstractExtensionPanel.java  |  39 ++++
 .../client/console/panels/StatusPanel.java      |   4 +-
 .../client/console/preview/BinaryPreview.java   |  34 ----
 .../client/console/preview/PreviewUtil.java     |  60 ------
 .../console/preview/PreviewerClassScanner.java  |  48 -----
 .../markup/html/form/BinaryFieldPanel.java      |   2 +-
 .../html/form/preview/BinaryCertPreviewer.java  |   2 +-
 .../html/form/preview/BinaryImagePreviewer.java |   2 +-
 .../src/main/resources/authorizations.xml       |   2 +-
 .../src/main/resources/consoleContext.xml       |   4 +-
 .../client/console/pages/Configuration.html     |   3 +
 .../console/pages/Configuration.properties      |   1 +
 .../console/pages/Configuration_it.properties   |   1 +
 .../pages/Configuration_pt_BR.properties        |   1 +
 syncope620/ext/camel/client-console/pom.xml     |  77 ++++++++
 .../console/pages/CamelRouteModalPage.java      |  89 +++++++++
 .../client/console/panels/CamelRoutePanel.java  | 181 +++++++++++++++++++
 .../console/rest/CamelRouteRestClient.java      |  53 ++++++
 .../console/pages/CamelRouteModalPage.html      |  66 +++++++
 .../pages/CamelRouteModalPage.properties        |  17 ++
 .../pages/CamelRouteModalPage_it.properties     |  17 ++
 .../pages/CamelRouteModalPage_pt_BR.properties  |  17 ++
 .../client/console/panels/CamelRoutePanel.html  |  28 +++
 syncope620/ext/camel/pom.xml                    |   1 +
 syncope620/fit/console-reference/pom.xml        |  14 +-
 .../init/ImplementationClassNamesLoader.java    |   4 -
 34 files changed, 938 insertions(+), 255 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/client/console/src/main/java/org/apache/syncope/client/console/BinaryPreview.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/BinaryPreview.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/BinaryPreview.java
new file mode 100644
index 0000000..c499d60
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/BinaryPreview.java
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface BinaryPreview {
+
+    public String[] mimeTypes() default {};
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/client/console/src/main/java/org/apache/syncope/client/console/ExtensionPanel.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/ExtensionPanel.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/ExtensionPanel.java
new file mode 100644
index 0000000..5574926
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/ExtensionPanel.java
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface ExtensionPanel {
+
+    public String value();
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/client/console/src/main/java/org/apache/syncope/client/console/SyncopeApplication.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/SyncopeApplication.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/SyncopeApplication.java
index daebd45..ac44d43 100644
--- a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/SyncopeApplication.java
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/SyncopeApplication.java
@@ -19,6 +19,7 @@
 package org.apache.syncope.client.console;
 
 import java.io.Serializable;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.commons.Constants;
 import org.apache.syncope.client.console.commons.XMLRolesReader;
 import org.apache.syncope.client.console.pages.Configuration;
@@ -160,70 +161,70 @@ public class SyncopeApplication
         };
         page.add(infoLink);
 
-        BookmarkablePageLink<Page> schemaLink = new BookmarkablePageLink<Page>("schema", Schema.class);
+        BookmarkablePageLink<Page> schemaLink = new BookmarkablePageLink<>("schema", Schema.class);
         MetaDataRoleAuthorizationStrategy.authorize(
                 schemaLink, WebPage.ENABLE, xmlRolesReader.getEntitlement("Schema", "list"));
         page.add(schemaLink);
         schemaLink.add(new Image("schemaIcon", new ContextRelativeResource(IMG_PREFIX + (notsel
                 ? IMG_NOTSEL
-                : "") + "schema" + Constants.PNG_EXT)));
+                : StringUtils.EMPTY) + "schema" + Constants.PNG_EXT)));
 
-        BookmarkablePageLink<Page> usersLink = new BookmarkablePageLink<Page>("users", Users.class);
+        BookmarkablePageLink<Page> usersLink = new BookmarkablePageLink<>("users", Users.class);
         MetaDataRoleAuthorizationStrategy.authorize(
                 usersLink, WebPage.ENABLE, xmlRolesReader.getEntitlement("Users", "list"));
         page.add(usersLink);
         usersLink.add(new Image("usersIcon", new ContextRelativeResource(IMG_PREFIX + (notsel
                 ? IMG_NOTSEL
-                : "") + "users" + Constants.PNG_EXT)));
+                : StringUtils.EMPTY) + "users" + Constants.PNG_EXT)));
 
-        BookmarkablePageLink<Page> rolesLink = new BookmarkablePageLink<Page>("roles", Roles.class);
+        BookmarkablePageLink<Page> rolesLink = new BookmarkablePageLink<>("roles", Roles.class);
         MetaDataRoleAuthorizationStrategy.authorize(
                 rolesLink, WebPage.ENABLE, xmlRolesReader.getEntitlement("Roles", "list"));
         page.add(rolesLink);
         rolesLink.add(new Image("rolesIcon", new ContextRelativeResource(IMG_PREFIX + (notsel
                 ? IMG_NOTSEL
-                : "") + "roles" + Constants.PNG_EXT)));
+                : StringUtils.EMPTY) + "roles" + Constants.PNG_EXT)));
 
-        BookmarkablePageLink<Page> resourcesLink = new BookmarkablePageLink<Page>("resources", Resources.class);
+        BookmarkablePageLink<Page> resourcesLink = new BookmarkablePageLink<>("resources", Resources.class);
         MetaDataRoleAuthorizationStrategy.authorize(
                 resourcesLink, WebPage.ENABLE, xmlRolesReader.getEntitlement("Resources", "list"));
         page.add(resourcesLink);
         resourcesLink.add(new Image("resourcesIcon", new ContextRelativeResource(IMG_PREFIX + (notsel
                 ? IMG_NOTSEL
-                : "") + "resources" + Constants.PNG_EXT)));
+                : StringUtils.EMPTY) + "resources" + Constants.PNG_EXT)));
 
-        BookmarkablePageLink<Page> todoLink = new BookmarkablePageLink<Page>("todo", Todo.class);
+        BookmarkablePageLink<Page> todoLink = new BookmarkablePageLink<>("todo", Todo.class);
         MetaDataRoleAuthorizationStrategy.authorize(
                 todoLink, WebPage.ENABLE, xmlRolesReader.getEntitlement("Approval", "list"));
         page.add(todoLink);
         todoLink.add(new Image("todoIcon", new ContextRelativeResource(IMG_PREFIX + (notsel
                 ? IMG_NOTSEL
-                : "") + "todo" + Constants.PNG_EXT)));
+                : StringUtils.EMPTY) + "todo" + Constants.PNG_EXT)));
 
-        BookmarkablePageLink<Page> reportLink = new BookmarkablePageLink<Page>("reports", Reports.class);
+        BookmarkablePageLink<Page> reportLink = new BookmarkablePageLink<>("reports", Reports.class);
         MetaDataRoleAuthorizationStrategy.authorize(
                 reportLink, WebPage.ENABLE, xmlRolesReader.getEntitlement("Reports", "list"));
         page.add(reportLink);
         reportLink.add(new Image("reportsIcon", new ContextRelativeResource(IMG_PREFIX + (notsel
                 ? IMG_NOTSEL
-                : "") + "reports" + Constants.PNG_EXT)));
+                : StringUtils.EMPTY) + "reports" + Constants.PNG_EXT)));
 
-        BookmarkablePageLink<Page> configurationLink = new BookmarkablePageLink<Page>("configuration",
+        BookmarkablePageLink<Page> configurationLink = new BookmarkablePageLink<>("configuration",
                 Configuration.class);
         MetaDataRoleAuthorizationStrategy.authorize(
                 configurationLink, WebPage.ENABLE, xmlRolesReader.getEntitlement("Configuration", "list"));
         page.add(configurationLink);
         configurationLink.add(new Image("configurationIcon", new ContextRelativeResource(IMG_PREFIX + (notsel
                 ? IMG_NOTSEL
-                : "") + "configuration" + Constants.PNG_EXT)));
+                : StringUtils.EMPTY) + "configuration" + Constants.PNG_EXT)));
 
-        BookmarkablePageLink<Page> taskLink = new BookmarkablePageLink<Page>("tasks", Tasks.class);
+        BookmarkablePageLink<Page> taskLink = new BookmarkablePageLink<>("tasks", Tasks.class);
         MetaDataRoleAuthorizationStrategy.authorize(
                 taskLink, WebPage.ENABLE, xmlRolesReader.getEntitlement("Tasks", "list"));
         page.add(taskLink);
         taskLink.add(new Image("tasksIcon", new ContextRelativeResource(IMG_PREFIX + (notsel
                 ? IMG_NOTSEL
-                : "") + "tasks" + Constants.PNG_EXT)));
+                : StringUtils.EMPTY) + "tasks" + Constants.PNG_EXT)));
 
         page.add(new BookmarkablePageLink<Page>("logout", Logout.class));
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/client/console/src/main/java/org/apache/syncope/client/console/commons/PreviewUtil.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/commons/PreviewUtil.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/commons/PreviewUtil.java
new file mode 100644
index 0000000..98e47c0
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/commons/PreviewUtil.java
@@ -0,0 +1,62 @@
+/*
+ * 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.commons;
+
+import java.lang.reflect.InvocationTargetException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.init.ImplementationClassNamesLoader;
+import org.apache.syncope.client.console.wicket.markup.html.form.preview.AbstractBinaryPreviewer;
+import org.apache.wicket.Component;
+import org.apache.wicket.util.crypt.Base64;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.ClassUtils;
+
+@org.springframework.stereotype.Component
+public class PreviewUtil {
+
+    @Autowired
+    private ImplementationClassNamesLoader implementationClassNamesLoader;
+
+    public Component getPreviewer(final String mimeType, final String file)
+            throws InstantiationException, IllegalAccessException, InvocationTargetException {
+
+        final Class<? extends AbstractBinaryPreviewer> previewer = StringUtils.isBlank(file)
+                ? null
+                : implementationClassNamesLoader.getPreviewerClass(mimeType);
+
+        return previewer == null
+                ? null
+                : ClassUtils.getConstructorIfAvailable(previewer, String.class, String.class, byte[].class).
+                newInstance(new Object[] { "previewer", mimeType, Base64.decodeBase64(file) }).
+                preview();
+    }
+
+    public Component getPreviewer(final String mimeType, final byte[] file)
+            throws InstantiationException, IllegalAccessException, InvocationTargetException {
+
+        final Class<? extends AbstractBinaryPreviewer> previewer =
+                implementationClassNamesLoader.getPreviewerClass(mimeType);
+
+        return previewer == null
+                ? null
+                : ClassUtils.getConstructorIfAvailable(previewer, String.class, String.class, byte[].class).
+                newInstance(new Object[] { "previewer", mimeType, file }).
+                preview();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/client/console/src/main/java/org/apache/syncope/client/console/init/BinaryPreviewersLoader.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/init/BinaryPreviewersLoader.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/init/BinaryPreviewersLoader.java
deleted file mode 100644
index fd5bc35..0000000
--- a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/init/BinaryPreviewersLoader.java
+++ /dev/null
@@ -1,71 +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.client.console.init;
-
-import java.util.List;
-import org.apache.commons.lang3.ArrayUtils;
-import org.apache.syncope.client.console.preview.BinaryPreview;
-import org.apache.syncope.client.console.preview.PreviewerClassScanner;
-import org.apache.syncope.client.console.wicket.markup.html.form.preview.AbstractBinaryPreviewer;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-@Component
-public class BinaryPreviewersLoader implements SyncopeConsoleLoader {
-
-    /**
-     * Logger.
-     */
-    private static final Logger LOG = LoggerFactory.getLogger(BinaryPreviewersLoader.class);
-
-    @Autowired
-    private PreviewerClassScanner classScanner;
-
-    private List<Class<? extends AbstractBinaryPreviewer>> classes;
-
-    @Override
-    public Integer getPriority() {
-        return 0;
-    }
-
-    @Override
-    public void load() {
-        classes = classScanner.getComponentClasses();
-    }
-
-    public List<Class<? extends AbstractBinaryPreviewer>> getClasses() {
-        LOG.debug("Returning loaded classes: {}", classes);
-        return classes;
-    }
-
-    public Class<? extends AbstractBinaryPreviewer> getClass(final String mimeType) {
-        LOG.debug("Searching for previewer class for MIME type: {}", mimeType);
-        Class<? extends AbstractBinaryPreviewer> previewer = null;
-        for (Class<? extends AbstractBinaryPreviewer> candidate : classes) {
-            LOG.debug("Evaluating previewer class {} for MIME type {}", candidate.getName(), mimeType);
-            if (ArrayUtils.contains(candidate.getAnnotation(BinaryPreview.class).mimeTypes(), mimeType)) {
-                LOG.debug("Found existing previewer for MIME type {}: {}", mimeType, candidate.getName());
-                previewer = candidate;
-            }
-        }
-        return previewer;
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/client/console/src/main/java/org/apache/syncope/client/console/init/ImplementationClassNamesLoader.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/init/ImplementationClassNamesLoader.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/init/ImplementationClassNamesLoader.java
new file mode 100644
index 0000000..0ff7282
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/init/ImplementationClassNamesLoader.java
@@ -0,0 +1,109 @@
+/*
+ * 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.init;
+
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.panels.AbstractExtensionPanel;
+import org.apache.syncope.client.console.BinaryPreview;
+import org.apache.syncope.client.console.wicket.markup.html.form.preview.AbstractBinaryPreviewer;
+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;
+
+@Component
+public class ImplementationClassNamesLoader implements SyncopeConsoleLoader {
+
+    /**
+     * Logger.
+     */
+    private static final Logger LOG = LoggerFactory.getLogger(ImplementationClassNamesLoader.class);
+
+    private List<Class<? extends AbstractBinaryPreviewer>> previewers;
+
+    private List<Class<? extends AbstractExtensionPanel>> extPanels;
+
+    @Override
+    public Integer getPriority() {
+        return 0;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void load() {
+        previewers = new ArrayList<>();
+        extPanels = new ArrayList<>();
+
+        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
+        scanner.addIncludeFilter(new AssignableTypeFilter(AbstractBinaryPreviewer.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(AbstractExtensionPanel.class));
+
+        for (BeanDefinition bd : scanner.findCandidateComponents(StringUtils.EMPTY)) {
+            try {
+                Class<?> clazz = ClassUtils.resolveClassName(
+                        bd.getBeanClassName(), ClassUtils.getDefaultClassLoader());
+                boolean isAbsractClazz = Modifier.isAbstract(clazz.getModifiers());
+
+                if (AbstractBinaryPreviewer.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                    previewers.add((Class<? extends AbstractBinaryPreviewer>) clazz);
+                } else if (AbstractExtensionPanel.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                    extPanels.add((Class<? extends AbstractExtensionPanel>) clazz);
+                }
+
+            } catch (Throwable t) {
+                LOG.warn("Could not inspect class {}", bd.getBeanClassName(), t);
+            }
+        }
+        previewers = Collections.unmodifiableList(previewers);
+        extPanels = Collections.unmodifiableList(extPanels);
+
+        LOG.debug("Binary previewers found: {}", previewers);
+        LOG.debug("Extension panels found: {}", extPanels);
+    }
+
+    public Class<? extends AbstractBinaryPreviewer> getPreviewerClass(final String mimeType) {
+        LOG.debug("Searching for previewer class for MIME type: {}", mimeType);
+        Class<? extends AbstractBinaryPreviewer> previewer = null;
+        for (Class<? extends AbstractBinaryPreviewer> candidate : previewers) {
+            LOG.debug("Evaluating previewer class {} for MIME type {}", candidate.getName(), mimeType);
+            if (ArrayUtils.contains(candidate.getAnnotation(BinaryPreview.class).mimeTypes(), mimeType)) {
+                LOG.debug("Found existing previewer for MIME type {}: {}", mimeType, candidate.getName());
+                previewer = candidate;
+            }
+        }
+        return previewer;
+    }
+
+    public List<Class<? extends AbstractBinaryPreviewer>> getPreviewerClasses() {
+        return previewers;
+    }
+
+    public List<Class<? extends AbstractExtensionPanel>> getExtPanelClasses() {
+        return extPanels;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/client/console/src/main/java/org/apache/syncope/client/console/pages/Configuration.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/pages/Configuration.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/pages/Configuration.java
index ea964d8..c6774f2 100644
--- a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/pages/Configuration.java
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/pages/Configuration.java
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.client.console.pages;
 
+import static org.apache.syncope.client.console.pages.AbstractBasePage.LOG;
+
 import java.io.File;
 import java.io.Serializable;
 import java.util.ArrayList;
@@ -28,11 +30,15 @@ import java.util.List;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.core.LoggerContext;
 import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.syncope.client.console.ExtensionPanel;
 import org.apache.syncope.client.console.commons.AttrLayoutType;
 import org.apache.syncope.client.console.commons.Constants;
 import org.apache.syncope.client.console.commons.HttpResourceStream;
 import org.apache.syncope.client.console.commons.PreferenceManager;
 import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
+import org.apache.syncope.client.console.init.ImplementationClassNamesLoader;
+import org.apache.syncope.client.console.panels.AbstractExtensionPanel;
+import org.apache.syncope.client.console.panels.JQueryUITabbedPanel;
 import org.apache.syncope.client.console.panels.LayoutsPanel;
 import org.apache.syncope.client.console.panels.PoliciesPanel;
 import org.apache.syncope.client.console.rest.LoggerRestClient;
@@ -51,6 +57,7 @@ import org.apache.syncope.common.lib.to.SecurityQuestionTO;
 import org.apache.syncope.common.lib.types.LoggerLevel;
 import org.apache.syncope.common.lib.types.PolicyType;
 import org.apache.wicket.Page;
+import org.apache.wicket.PageReference;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
 import org.apache.wicket.ajax.markup.html.AjaxLink;
@@ -64,6 +71,7 @@ import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColu
 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.extensions.markup.html.repeater.util.SortableDataProvider;
+import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
 import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.form.DropDownChoice;
@@ -73,6 +81,7 @@ import org.apache.wicket.markup.html.link.BookmarkablePageLink;
 import org.apache.wicket.markup.html.link.Link;
 import org.apache.wicket.markup.html.list.ListItem;
 import org.apache.wicket.markup.html.list.PropertyListView;
+import org.apache.wicket.markup.html.panel.Panel;
 import org.apache.wicket.markup.repeater.Item;
 import org.apache.wicket.model.AbstractReadOnlyModel;
 import org.apache.wicket.model.IModel;
@@ -85,6 +94,7 @@ import org.apache.wicket.request.resource.ContentDisposition;
 import org.apache.wicket.request.resource.DynamicImageResource;
 import org.apache.wicket.request.resource.IResource;
 import org.apache.wicket.spring.injection.annot.SpringBean;
+import org.springframework.util.ClassUtils;
 import org.springframework.web.context.support.WebApplicationContextUtils;
 
 /**
@@ -121,6 +131,9 @@ public class Configuration extends BasePage {
     @SpringBean
     private PreferenceManager prefMan;
 
+    @SpringBean
+    private ImplementationClassNamesLoader implementationClassNamesLoader;
+
     private final ModalWindow syncopeConfWin;
 
     private final ModalWindow createNotificationWin;
@@ -140,6 +153,14 @@ public class Configuration extends BasePage {
     public Configuration() {
         super();
 
+        // Layouts
+        add(new LayoutsPanel("adminUserLayoutPanel", AttrLayoutType.ADMIN_USER, feedbackPanel));
+        add(new LayoutsPanel("selfUserLayoutPanel", AttrLayoutType.SELF_USER, feedbackPanel));
+        add(new LayoutsPanel("adminRoleLayoutPanel", AttrLayoutType.ADMIN_ROLE, feedbackPanel));
+        add(new LayoutsPanel("selfRoleLayoutPanel", AttrLayoutType.SELF_ROLE, feedbackPanel));
+        add(new LayoutsPanel("adminMembershipLayoutPanel", AttrLayoutType.ADMIN_MEMBERSHIP, feedbackPanel));
+        add(new LayoutsPanel("selfMembershipLayoutPanel", AttrLayoutType.SELF_MEMBERSHIP, feedbackPanel));
+
         add(syncopeConfWin = new ModalWindow("syncopeConfWin"));
         syncopeConfWin.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
         syncopeConfWin.setInitialHeight(SYNCOPECONF_WIN_HEIGHT);
@@ -190,7 +211,7 @@ public class Configuration extends BasePage {
         }
 
         BookmarkablePageLink<Void> activitiModeler =
-                new BookmarkablePageLink<Void>("activitiModeler", ActivitiModelerPopupPage.class);
+                new BookmarkablePageLink<>("activitiModeler", ActivitiModelerPopupPage.class);
         activitiModeler.setPopupSettings(new VeilPopupSettings().setHeight(600).setWidth(800));
         MetaDataRoleAuthorizationStrategy.authorize(activitiModeler, ENABLE,
                 xmlRolesReader.getEntitlement("Configuration", "workflowDefRead"));
@@ -260,12 +281,8 @@ public class Configuration extends BasePage {
                 consoleLoggerContainer, ENABLE, xmlRolesReader.getEntitlement("Configuration", "logList"));
         add(consoleLoggerContainer);
 
-        add(new LayoutsPanel("adminUserLayoutPanel", AttrLayoutType.ADMIN_USER, feedbackPanel));
-        add(new LayoutsPanel("selfUserLayoutPanel", AttrLayoutType.SELF_USER, feedbackPanel));
-        add(new LayoutsPanel("adminRoleLayoutPanel", AttrLayoutType.ADMIN_ROLE, feedbackPanel));
-        add(new LayoutsPanel("selfRoleLayoutPanel", AttrLayoutType.SELF_ROLE, feedbackPanel));
-        add(new LayoutsPanel("adminMembershipLayoutPanel", AttrLayoutType.ADMIN_MEMBERSHIP, feedbackPanel));
-        add(new LayoutsPanel("selfMembershipLayoutPanel", AttrLayoutType.SELF_MEMBERSHIP, feedbackPanel));
+        // Extension panels
+        setupExtPanels();
     }
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
@@ -401,7 +418,7 @@ public class Configuration extends BasePage {
         });
 
         final AjaxFallbackDefaultDataTable<NotificationTO, String> notificationTable =
-                new AjaxFallbackDefaultDataTable<NotificationTO, String>(
+                new AjaxFallbackDefaultDataTable<>(
                         "notificationTable", notificationCols, new NotificationProvider(), notificationPaginatorRows);
 
         notificationContainer = new WebMarkupContainer("notificationContainer");
@@ -533,7 +550,7 @@ public class Configuration extends BasePage {
         });
 
         final AjaxFallbackDefaultDataTable<SecurityQuestionTO, String> securityQuestionTable =
-                new AjaxFallbackDefaultDataTable<SecurityQuestionTO, String>("securityQuestionTable",
+                new AjaxFallbackDefaultDataTable<>("securityQuestionTable",
                         securityQuestionCols, new SecurityQuestionProvider(), 50);
 
         securityQuestionContainer = new WebMarkupContainer("securityQuestionContainer");
@@ -572,6 +589,48 @@ public class Configuration extends BasePage {
         add(createSecurityQuestionLink);
     }
 
+    private void setupExtPanels() {
+        List<AbstractTab> tabs = new ArrayList<>();
+        int index = 0;
+        for (final Class<? extends AbstractExtensionPanel> clazz
+                : implementationClassNamesLoader.getExtPanelClasses()) {
+
+            String title = clazz.getAnnotation(ExtensionPanel.class) == null
+                    ? "Extension " + index
+                    : clazz.getAnnotation(ExtensionPanel.class).value();
+            tabs.add(new AbstractTab(new Model<>(title)) {
+
+                private static final long serialVersionUID = -5861786415855103549L;
+
+                @Override
+                public WebMarkupContainer getPanel(final String panelId) {
+                    Panel panel;
+
+                    try {
+                        panel = ClassUtils.getConstructorIfAvailable(clazz, String.class, PageReference.class).
+                                newInstance(panelId, Configuration.this.getPageReference());
+                    } catch (Exception e) {
+                        panel = new Panel(panelId) {
+
+                            private static final long serialVersionUID = 5538299138211283825L;
+
+                        };
+
+                        LOG.error("Could not instantiate {}", clazz.getName(), e);
+                    }
+
+                    return panel;
+                }
+            });
+
+            index++;
+        }
+
+        JQueryUITabbedPanel<AbstractTab> extPanels = new JQueryUITabbedPanel<>("extPanels", tabs);
+        extPanels.setVisible(!tabs.isEmpty());
+        add(extPanels);
+    }
+
     private class NotificationProvider extends SortableDataProvider<NotificationTO, String> {
 
         private static final long serialVersionUID = -276043813563988590L;
@@ -581,7 +640,7 @@ public class Configuration extends BasePage {
         public NotificationProvider() {
             //Default sorting
             setSort("key", SortOrder.ASCENDING);
-            comparator = new SortableDataProviderComparator<NotificationTO>(this);
+            comparator = new SortableDataProviderComparator<>(this);
         }
 
         @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/client/console/src/main/java/org/apache/syncope/client/console/pages/Schema.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/pages/Schema.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/pages/Schema.java
index 2e26ded..c47edcd 100644
--- a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/pages/Schema.java
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/pages/Schema.java
@@ -148,13 +148,13 @@ public class Schema extends BasePage {
         for (final AttributableType attrType : AttributableType.values()) {
             final String attrTypeAsString = attrType.name().toLowerCase();
 
-            List<ITab> tabs = new ArrayList<ITab>();
+            List<ITab> tabs = new ArrayList<>();
 
             for (final SchemaType schemaType : SchemaType.values()) {
                 if (attrType != AttributableType.CONFIGURATION || schemaType == SchemaType.PLAIN) {
                     final String schemaTypeAsString = schemaType.name().toLowerCase();
 
-                    tabs.add(new AbstractTab(new Model<String>(getString(schemaTypeAsString))) {
+                    tabs.add(new AbstractTab(new Model<>(getString(schemaTypeAsString))) {
 
                         private static final long serialVersionUID = -5861786415855103549L;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/AbstractExtensionPanel.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/AbstractExtensionPanel.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/AbstractExtensionPanel.java
new file mode 100644
index 0000000..a785e9f
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/AbstractExtensionPanel.java
@@ -0,0 +1,39 @@
+/*
+ * 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 org.apache.syncope.client.console.commons.XMLRolesReader;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.spring.injection.annot.SpringBean;
+
+public abstract class AbstractExtensionPanel extends Panel {
+
+    private static final long serialVersionUID = 4627828052717627159L;
+
+    @SpringBean
+    protected XMLRolesReader xmlRolesReader;
+
+    protected PageReference pageref;
+
+    public AbstractExtensionPanel(final String id, final PageReference pageref) {
+        super(id);
+        this.pageref = pageref;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/StatusPanel.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/StatusPanel.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/StatusPanel.java
index e8b3af7..4faea4e 100644
--- a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/StatusPanel.java
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/StatusPanel.java
@@ -111,8 +111,8 @@ public class StatusPanel extends Panel implements IHeaderContributor {
 
         connObjects = statusUtils.getConnectorObjects(subject);
 
-        final List<StatusBean> statusBeans = new ArrayList<StatusBean>(connObjects.size() + 1);
-        initialStatusBeanMap = new LinkedHashMap<String, StatusBean>(connObjects.size() + 1);
+        final List<StatusBean> statusBeans = new ArrayList<>(connObjects.size() + 1);
+        initialStatusBeanMap = new LinkedHashMap<>(connObjects.size() + 1);
 
         final StatusBean syncope = new StatusBean(subject, "syncope");
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/client/console/src/main/java/org/apache/syncope/client/console/preview/BinaryPreview.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/preview/BinaryPreview.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/preview/BinaryPreview.java
deleted file mode 100644
index bc5998a..0000000
--- a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/preview/BinaryPreview.java
+++ /dev/null
@@ -1,34 +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.client.console.preview;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Target({ ElementType.TYPE })
-@Retention(RetentionPolicy.RUNTIME)
-@Documented
-public @interface BinaryPreview {
-
-    public String[] mimeTypes() default {};
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/client/console/src/main/java/org/apache/syncope/client/console/preview/PreviewUtil.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/preview/PreviewUtil.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/preview/PreviewUtil.java
deleted file mode 100644
index 3ebc28a..0000000
--- a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/preview/PreviewUtil.java
+++ /dev/null
@@ -1,60 +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.client.console.preview;
-
-import java.lang.reflect.InvocationTargetException;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.client.console.init.BinaryPreviewersLoader;
-import org.apache.syncope.client.console.wicket.markup.html.form.preview.AbstractBinaryPreviewer;
-import org.apache.wicket.Component;
-import org.apache.wicket.util.crypt.Base64;
-import org.springframework.beans.factory.annotation.Autowired;
-
-@org.springframework.stereotype.Component
-public class PreviewUtil {
-
-    @Autowired
-    private BinaryPreviewersLoader previewPanelClassInitializer;
-
-    public Component getPreviewer(final String mimeType, final String file) throws ClassNotFoundException,
-            NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
-
-        final Class<? extends AbstractBinaryPreviewer> previewer = StringUtils.isBlank(file)
-                ? null
-                : previewPanelClassInitializer.getClass(mimeType);
-
-        return previewer == null
-                ? null
-                : ((AbstractBinaryPreviewer) Class.forName(previewer.getName()).
-                getConstructor(String.class, String.class, byte[].class).newInstance(
-                        new Object[] { "previewer", mimeType, Base64.decodeBase64(file) })).preview();
-    }
-
-    public Component getPreviewer(final String mimeType, final byte[] file) throws ClassNotFoundException,
-            NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
-
-        final Class<? extends AbstractBinaryPreviewer> previewer = previewPanelClassInitializer.getClass(mimeType);
-
-        return previewer == null
-                ? null
-                : ((AbstractBinaryPreviewer) Class.forName(previewer.getName()).
-                getConstructor(String.class, String.class, byte[].class).newInstance(
-                        new Object[] { "previewer", mimeType, file })).preview();
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/client/console/src/main/java/org/apache/syncope/client/console/preview/PreviewerClassScanner.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/preview/PreviewerClassScanner.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/preview/PreviewerClassScanner.java
deleted file mode 100644
index a70f9cf..0000000
--- a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/preview/PreviewerClassScanner.java
+++ /dev/null
@@ -1,48 +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.client.console.preview;
-
-import java.util.ArrayList;
-import java.util.List;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.client.console.wicket.markup.html.form.preview.AbstractBinaryPreviewer;
-import org.springframework.beans.factory.config.BeanDefinition;
-import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
-import org.springframework.core.type.filter.AnnotationTypeFilter;
-import org.springframework.stereotype.Component;
-import org.springframework.util.ClassUtils;
-
-@Component
-public class PreviewerClassScanner extends ClassPathScanningCandidateComponentProvider {
-
-    public PreviewerClassScanner() {
-        super(false);
-        addIncludeFilter(new AnnotationTypeFilter(BinaryPreview.class));
-    }
-
-    @SuppressWarnings("unchecked")
-    public final List<Class<? extends AbstractBinaryPreviewer>> getComponentClasses() {
-        List<Class<? extends AbstractBinaryPreviewer>> classes = new ArrayList<>();
-        for (BeanDefinition candidate : findCandidateComponents(StringUtils.EMPTY)) {
-            classes.add((Class<AbstractBinaryPreviewer>) ClassUtils.resolveClassName(candidate.getBeanClassName(),
-                    ClassUtils.getDefaultClassLoader()));
-        }
-        return classes;
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java
index df91c7d..949cc83 100644
--- a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java
@@ -22,7 +22,7 @@ import java.io.ByteArrayInputStream;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.client.console.preview.PreviewUtil;
+import org.apache.syncope.client.console.commons.PreviewUtil;
 import org.apache.syncope.client.console.commons.HttpResourceStream;
 import org.apache.syncope.client.console.commons.Constants;
 import org.apache.syncope.client.console.pages.BaseModalPage;

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/preview/BinaryCertPreviewer.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/preview/BinaryCertPreviewer.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/preview/BinaryCertPreviewer.java
index c5b9164..61589d7 100644
--- a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/preview/BinaryCertPreviewer.java
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/preview/BinaryCertPreviewer.java
@@ -24,7 +24,7 @@ import java.security.cert.X509Certificate;
 import javax.naming.ldap.LdapName;
 import javax.naming.ldap.Rdn;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.client.console.preview.BinaryPreview;
+import org.apache.syncope.client.console.BinaryPreview;
 import org.apache.wicket.Component;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.model.Model;

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/preview/BinaryImagePreviewer.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/preview/BinaryImagePreviewer.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/preview/BinaryImagePreviewer.java
index 6ab90a7..35e442e 100644
--- a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/preview/BinaryImagePreviewer.java
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/preview/BinaryImagePreviewer.java
@@ -18,7 +18,7 @@
  */
 package org.apache.syncope.client.console.wicket.markup.html.form.preview;
 
-import org.apache.syncope.client.console.preview.BinaryPreview;
+import org.apache.syncope.client.console.BinaryPreview;
 import org.apache.wicket.Component;
 import org.apache.wicket.extensions.markup.html.image.resource.ThumbnailImageResource;
 import org.apache.wicket.markup.html.image.NonCachingImage;

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/client/console/src/main/resources/authorizations.xml
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/resources/authorizations.xml b/syncope620/client/console/src/main/resources/authorizations.xml
index d5914f8..4610abf 100644
--- a/syncope620/client/console/src/main/resources/authorizations.xml
+++ b/syncope620/client/console/src/main/resources/authorizations.xml
@@ -317,7 +317,7 @@ under the License.
       <entitlement>POLICY_UPDATE</entitlement>
     </action>
   </page>
-  <page id="Routes">
+  <page id="CamelRoutes">
     <action id="list">
       <entitlement>ROUTE_LIST</entitlement>
     </action>

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/client/console/src/main/resources/consoleContext.xml
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/resources/consoleContext.xml b/syncope620/client/console/src/main/resources/consoleContext.xml
index bb7f94b..fbab64b 100644
--- a/syncope620/client/console/src/main/resources/consoleContext.xml
+++ b/syncope620/client/console/src/main/resources/consoleContext.xml
@@ -25,9 +25,7 @@ under the License.
                            http://www.springframework.org/schema/context
                            http://www.springframework.org/schema/context/spring-context.xsd">
 
-  <context:component-scan base-package="org.apache.syncope.client.console.rest"/>
-  <context:component-scan base-package="org.apache.syncope.client.console.init"/>
-  <context:component-scan base-package="org.apache.syncope.client.console.preview"/>
+  <context:component-scan base-package="org.apache.syncope.client.console"/>
 
   <bean id="confDirectoryPropertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
     <property name="order" value="1"/>

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/client/console/src/main/resources/org/apache/syncope/client/console/pages/Configuration.html
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/resources/org/apache/syncope/client/console/pages/Configuration.html b/syncope620/client/console/src/main/resources/org/apache/syncope/client/console/pages/Configuration.html
index 1bec3b7..931bf2b 100644
--- a/syncope620/client/console/src/main/resources/org/apache/syncope/client/console/pages/Configuration.html
+++ b/syncope620/client/console/src/main/resources/org/apache/syncope/client/console/pages/Configuration.html
@@ -39,6 +39,7 @@ under the License.
         <li><a href="#securityQuestions"><span><wicket:message key="securityQuestions"/></span></a></li>
         <li><a href="#workflow"><span><wicket:message key="workflow"/></span></a></li>
         <li><a href="#logs"><span><wicket:message key="logs"/></span></a></li>
+        <li><a href="#ext"><span><wicket:message key="ext"/></span></a></li>
       </ul>
       <div id="layouts">
         <ul>
@@ -208,6 +209,8 @@ under the License.
           </div>
         </div>
       </div>
+      <div id="ext" wicket:id="extPanels">
+      </div>
       <div>
         <div wicket:id="parameters">
           <a style="position: absolute; top: 2px; right:50px;" wicket:id="confLink">

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/client/console/src/main/resources/org/apache/syncope/client/console/pages/Configuration.properties
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/resources/org/apache/syncope/client/console/pages/Configuration.properties b/syncope620/client/console/src/main/resources/org/apache/syncope/client/console/pages/Configuration.properties
index 1b6bcc7..19d534f 100644
--- a/syncope620/client/console/src/main/resources/org/apache/syncope/client/console/pages/Configuration.properties
+++ b/syncope620/client/console/src/main/resources/org/apache/syncope/client/console/pages/Configuration.properties
@@ -45,3 +45,4 @@ adminRole=Administrator Role Form
 selfRole=Self Role Form
 adminMembership=Administrator Membership Form
 selfMembership=Self Membership Form
+ext=Extensions

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/client/console/src/main/resources/org/apache/syncope/client/console/pages/Configuration_it.properties
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/resources/org/apache/syncope/client/console/pages/Configuration_it.properties b/syncope620/client/console/src/main/resources/org/apache/syncope/client/console/pages/Configuration_it.properties
index 4969e24..0f13761 100644
--- a/syncope620/client/console/src/main/resources/org/apache/syncope/client/console/pages/Configuration_it.properties
+++ b/syncope620/client/console/src/main/resources/org/apache/syncope/client/console/pages/Configuration_it.properties
@@ -45,3 +45,4 @@ adminRole=Form Amministrazione Ruolo
 selfRole=Form Ruolo
 adminMembership=Form Amministrazione Membership
 selfMembership=Form Membership
+ext=Estensioni

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/client/console/src/main/resources/org/apache/syncope/client/console/pages/Configuration_pt_BR.properties
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/resources/org/apache/syncope/client/console/pages/Configuration_pt_BR.properties b/syncope620/client/console/src/main/resources/org/apache/syncope/client/console/pages/Configuration_pt_BR.properties
index cc2dd2b..63f19b7 100644
--- a/syncope620/client/console/src/main/resources/org/apache/syncope/client/console/pages/Configuration_pt_BR.properties
+++ b/syncope620/client/console/src/main/resources/org/apache/syncope/client/console/pages/Configuration_pt_BR.properties
@@ -45,3 +45,4 @@ adminRole=Forma Fun\u00e7\u00e3o de administra\u00e7\u00e3o
 selfRole=Form Fun\u00e7\u00e3o
 adminMembership=Forma de membro de administra\u00e7\u00e3o
 selfMembership=Forma Membro
+ext=Extens\u00f5es

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/ext/camel/client-console/pom.xml
----------------------------------------------------------------------
diff --git a/syncope620/ext/camel/client-console/pom.xml b/syncope620/ext/camel/client-console/pom.xml
new file mode 100644
index 0000000..8ee8173
--- /dev/null
+++ b/syncope620/ext/camel/client-console/pom.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-camel</artifactId>
+    <version>2.0.0-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Extensions: Camel Client Console</name>
+  <description>Apache Syncope Extensions: Camel Client Console</description>
+  <groupId>org.apache.syncope.ext.camel</groupId>
+  <artifactId>syncope-ext-camel-client-console</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.syncope.ext.camel</groupId>
+      <artifactId>syncope-ext-camel-common-lib</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.camel</groupId>
+      <artifactId>syncope-ext-camel-rest-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.client</groupId>
+      <artifactId>syncope-client-console</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-pmd-plugin</artifactId>
+      </plugin>
+    </plugins>
+    
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>true</filtering>
+      </resource>
+    </resources>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/ext/camel/client-console/src/main/java/org/apache/syncope/client/console/pages/CamelRouteModalPage.java
----------------------------------------------------------------------
diff --git a/syncope620/ext/camel/client-console/src/main/java/org/apache/syncope/client/console/pages/CamelRouteModalPage.java b/syncope620/ext/camel/client-console/src/main/java/org/apache/syncope/client/console/pages/CamelRouteModalPage.java
new file mode 100644
index 0000000..8650e07
--- /dev/null
+++ b/syncope620/ext/camel/client-console/src/main/java/org/apache/syncope/client/console/pages/CamelRouteModalPage.java
@@ -0,0 +1,89 @@
+/*
+ * 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 org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.rest.CamelRouteRestClient;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.CamelRouteTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.TextArea;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.spring.injection.annot.SpringBean;
+
+public class CamelRouteModalPage extends BaseModalPage {
+
+    private static final long serialVersionUID = -1438441210568592931L;
+
+    @SpringBean
+    private CamelRouteRestClient restClient;
+
+    public CamelRouteModalPage(final PageReference pageRef, final ModalWindow window,
+            final CamelRouteTO routeTO, final boolean createFlag) {
+
+        Form<CamelRouteTO> routeForm = new Form<>("routeDefForm");
+
+        final TextArea<String> routeDefArea =
+                new TextArea<>("content", new PropertyModel<String>(routeTO, "content"));
+
+        routeForm.add(routeDefArea);
+        routeForm.setModel(new CompoundPropertyModel<>(routeTO));
+
+        AjaxButton submit =
+                new IndicatingAjaxButton(APPLY, new Model<>(getString(SUBMIT)), routeForm) {
+
+                    private static final long serialVersionUID = -958724007591692537L;
+
+                    @Override
+                    protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+                        try {
+                            restClient.update(routeTO.getKey(), ((CamelRouteTO) form.getModelObject()).getContent());
+                            info(getString(Constants.OPERATION_SUCCEEDED));
+
+                            Configuration callerPage = (Configuration) pageRef.getPage();
+                            callerPage.setModalResult(true);
+                            window.close(target);
+                        } catch (SyncopeClientException scee) {
+                            error(getString(Constants.ERROR) + ": " + scee.getMessage());
+                        }
+                        target.add(feedbackPanel);
+                    }
+
+                    @Override
+                    protected void onError(final AjaxRequestTarget target, final Form<?> form) {
+                        target.add(feedbackPanel);
+                    }
+
+                };
+
+        MetaDataRoleAuthorizationStrategy.authorize(submit, ENABLE,
+                xmlRolesReader.getEntitlement("CamelRoutes", "update"));
+        routeForm.add(submit);
+
+        this.add(routeForm);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/ext/camel/client-console/src/main/java/org/apache/syncope/client/console/panels/CamelRoutePanel.java
----------------------------------------------------------------------
diff --git a/syncope620/ext/camel/client-console/src/main/java/org/apache/syncope/client/console/panels/CamelRoutePanel.java b/syncope620/ext/camel/client-console/src/main/java/org/apache/syncope/client/console/panels/CamelRoutePanel.java
new file mode 100644
index 0000000..e96dab5
--- /dev/null
+++ b/syncope620/ext/camel/client-console/src/main/java/org/apache/syncope/client/console/panels/CamelRoutePanel.java
@@ -0,0 +1,181 @@
+/*
+ * 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.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.syncope.client.console.ExtensionPanel;
+import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
+import org.apache.syncope.client.console.pages.CamelRouteModalPage;
+import org.apache.syncope.client.console.rest.CamelRouteRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksPanel;
+import org.apache.syncope.common.lib.to.CamelRouteTO;
+import org.apache.syncope.common.lib.types.SubjectType;
+import org.apache.wicket.Page;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+import org.apache.wicket.extensions.ajax.markup.html.repeater.data.table.AjaxFallbackDefaultDataTable;
+import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
+import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
+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.extensions.markup.html.repeater.util.SortableDataProvider;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.model.AbstractReadOnlyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.spring.injection.annot.SpringBean;
+
+@ExtensionPanel("Camel routes")
+public class CamelRoutePanel extends AbstractExtensionPanel {
+
+    private static final long serialVersionUID = 1965360932245590233L;
+
+    private static final int CAMELROUTE_WIN_HEIGHT = 480;
+
+    private static final int CAMELROUTE_WIN_WIDTH = 800;
+
+    @SpringBean
+    private CamelRouteRestClient restClient;
+
+    private ModalWindow editCamelRouteWin;
+
+    public CamelRoutePanel(final String id, final PageReference pageref) {
+        super(id, pageref);
+
+        editCamelRouteWin = new ModalWindow("editCamelRouteWin");
+        editCamelRouteWin.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
+        editCamelRouteWin.setInitialHeight(CAMELROUTE_WIN_HEIGHT);
+        editCamelRouteWin.setInitialWidth(CAMELROUTE_WIN_WIDTH);
+        editCamelRouteWin.setCookieName("editCamelRouteWin-modal");
+        add(editCamelRouteWin);
+
+        List<IColumn<CamelRouteTO, String>> routeCols = new ArrayList<>();
+        routeCols.add(new PropertyColumn<CamelRouteTO, String>(new ResourceModel("key"), "key", "key"));
+        routeCols.add(new AbstractColumn<CamelRouteTO, String>(new ResourceModel("actions", "")) {
+
+            private static final long serialVersionUID = 2054811145491901166L;
+
+            @Override
+            public String getCssClass() {
+                return "action";
+            }
+
+            @Override
+            public void populateItem(final Item<ICellPopulator<CamelRouteTO>> cellItem, final String componentId,
+                    final IModel<CamelRouteTO> model) {
+
+                final ActionLinksPanel panel = new ActionLinksPanel(componentId, model, pageref);
+
+                panel.add(new ActionLink() {
+
+                    private static final long serialVersionUID = -3722207913631435501L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target) {
+
+                        editCamelRouteWin.setPageCreator(new ModalWindow.PageCreator() {
+
+                            private static final long serialVersionUID = -7834632442532690940L;
+
+                            @Override
+                            public Page createPage() {
+                                return new CamelRouteModalPage(pageref, editCamelRouteWin,
+                                        restClient.read(model.getObject().getKey()), false);
+                            }
+
+                        });
+
+                        editCamelRouteWin.show(target);
+                    }
+                }, ActionLink.ActionType.EDIT, "CamelRoutes");
+
+                cellItem.add(panel);
+            }
+        });
+
+        final AjaxFallbackDefaultDataTable<CamelRouteTO, String> routeTable =
+                new AjaxFallbackDefaultDataTable<>("camelRouteTable", routeCols, new CamelRouteProvider(), 50);
+
+        WebMarkupContainer routeContainer = new WebMarkupContainer("camelRoutesContainer");
+        routeContainer.add(routeTable);
+        routeContainer.setOutputMarkupId(true);
+        MetaDataRoleAuthorizationStrategy.authorize(
+                routeContainer, ENABLE, xmlRolesReader.getEntitlement("CamelRoutes", "list"));
+        add(routeContainer);
+    }
+
+    private class CamelRouteProvider extends SortableDataProvider<CamelRouteTO, String> {
+
+        private static final long serialVersionUID = -2917236020432105949L;
+
+        private final SortableDataProviderComparator<CamelRouteTO> comparator;
+
+        public CamelRouteProvider() {
+            setSort("key", SortOrder.ASCENDING);
+            comparator = new SortableDataProviderComparator<>(this);
+        }
+
+        @Override
+        public Iterator<? extends CamelRouteTO> iterator(final long first, final long count) {
+            List<CamelRouteTO> list = new ArrayList<>();
+            if (restClient.isCamelEnabledFor(SubjectType.USER)) {
+                list.addAll(restClient.list(SubjectType.USER));
+            }
+            if (restClient.isCamelEnabledFor(SubjectType.ROLE)) {
+                list.addAll(restClient.list(SubjectType.ROLE));
+            }
+
+            Collections.sort(list, comparator);
+
+            return list.subList((int) first, (int) first + (int) count).iterator();
+        }
+
+        @Override
+        public long size() {
+            return (restClient.isCamelEnabledFor(SubjectType.USER)
+                    ? restClient.list(SubjectType.USER).size()
+                    : 0)
+                    + (restClient.isCamelEnabledFor(SubjectType.ROLE)
+                            ? restClient.list(SubjectType.ROLE).size()
+                            : 0);
+        }
+
+        @Override
+        public IModel<CamelRouteTO> model(final CamelRouteTO route) {
+            return new AbstractReadOnlyModel<CamelRouteTO>() {
+
+                private static final long serialVersionUID = 774694801558497248L;
+
+                @Override
+                public CamelRouteTO getObject() {
+                    return route;
+                }
+            };
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/ext/camel/client-console/src/main/java/org/apache/syncope/client/console/rest/CamelRouteRestClient.java
----------------------------------------------------------------------
diff --git a/syncope620/ext/camel/client-console/src/main/java/org/apache/syncope/client/console/rest/CamelRouteRestClient.java b/syncope620/ext/camel/client-console/src/main/java/org/apache/syncope/client/console/rest/CamelRouteRestClient.java
new file mode 100644
index 0000000..509f208
--- /dev/null
+++ b/syncope620/ext/camel/client-console/src/main/java/org/apache/syncope/client/console/rest/CamelRouteRestClient.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.client.console.rest;
+
+import java.util.List;
+import org.apache.syncope.client.console.SyncopeSession;
+import org.apache.syncope.common.lib.to.CamelRouteTO;
+import org.apache.syncope.common.lib.types.SubjectType;
+import org.apache.syncope.common.rest.api.service.CamelRouteService;
+import org.springframework.stereotype.Component;
+
+@Component
+public class CamelRouteRestClient extends BaseRestClient {
+
+    private static final long serialVersionUID = -2018208424159468912L;
+
+    public List<CamelRouteTO> list(final SubjectType subject) {
+        return getService(CamelRouteService.class).list(subject);
+    }
+
+    public CamelRouteTO read(final String key) {
+        return getService(CamelRouteService.class).read(key);
+    }
+
+    public void update(final String key, final String content) {
+        CamelRouteTO routeTO = read(key);
+        routeTO.setContent(content);
+        getService(CamelRouteService.class).update(key, routeTO);
+    }
+
+    public boolean isCamelEnabledFor(final SubjectType subjectType) {
+        return subjectType == SubjectType.USER
+                ? SyncopeSession.get().getSyncopeTO().getUserProvisioningManager().indexOf("Camel") != -1
+                : SyncopeSession.get().getSyncopeTO().getRoleProvisioningManager().indexOf("Camel") != -1;
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/ext/camel/client-console/src/main/resources/org/apache/syncope/client/console/pages/CamelRouteModalPage.html
----------------------------------------------------------------------
diff --git a/syncope620/ext/camel/client-console/src/main/resources/org/apache/syncope/client/console/pages/CamelRouteModalPage.html b/syncope620/ext/camel/client-console/src/main/resources/org/apache/syncope/client/console/pages/CamelRouteModalPage.html
new file mode 100644
index 0000000..63ae4f4
--- /dev/null
+++ b/syncope620/ext/camel/client-console/src/main/resources/org/apache/syncope/client/console/pages/CamelRouteModalPage.html
@@ -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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:head>
+    <link rel="stylesheet" type="text/css" href="webjars/codemirror/${codemirror.version}/lib/codemirror.css"/>
+
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/lib/codemirror.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/mode/xml/xml.js"></script>
+    <script type="text/javascript">
+      function updateTextArea(editor) {
+        document.getElementById("routeDefForm").elements["content"].value = editor.getValue();
+      }
+    </script>
+    <style>
+      .w_content_3 {
+        padding: 0;
+        color: #333333;
+        font-family: Verdana,Tahoma,sans-serif;
+        font-size: 100%;
+        border: 1px solid #BBBBBB;
+        padding: 1%;
+      }
+    </style>
+  </wicket:head>
+  <wicket:extend>
+    <div style="padding: 1%;">
+      <div class="w_content_3">
+        <p class="ui-widget ui-corner-all ui-widget-header">
+          <wicket:message key="title"/>
+        </p>
+        <form wicket:id="routeDefForm" id="routeDefForm">
+          <textarea wicket:id="content" id="content" name="content" style="width: 100%; height: 350px;">
+          </textarea>
+          <div style="margin: 10px;">
+            <input type="submit"
+                   class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"
+                   wicket:id="apply"
+                   onclick=""/>
+          </div>
+        </form>
+      </div>
+    </div>
+    <script>
+      window.onload = function () {
+        var editor = CodeMirror.fromTextArea(document.getElementById("content"), {lineNumbers: true});
+        editor.on("change", updateTextArea);
+      }
+    </script>
+  </wicket:extend>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/ext/camel/client-console/src/main/resources/org/apache/syncope/client/console/pages/CamelRouteModalPage.properties
----------------------------------------------------------------------
diff --git a/syncope620/ext/camel/client-console/src/main/resources/org/apache/syncope/client/console/pages/CamelRouteModalPage.properties b/syncope620/ext/camel/client-console/src/main/resources/org/apache/syncope/client/console/pages/CamelRouteModalPage.properties
new file mode 100644
index 0000000..8273ee0
--- /dev/null
+++ b/syncope620/ext/camel/client-console/src/main/resources/org/apache/syncope/client/console/pages/CamelRouteModalPage.properties
@@ -0,0 +1,17 @@
+# 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.
+title=Camel route

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/ext/camel/client-console/src/main/resources/org/apache/syncope/client/console/pages/CamelRouteModalPage_it.properties
----------------------------------------------------------------------
diff --git a/syncope620/ext/camel/client-console/src/main/resources/org/apache/syncope/client/console/pages/CamelRouteModalPage_it.properties b/syncope620/ext/camel/client-console/src/main/resources/org/apache/syncope/client/console/pages/CamelRouteModalPage_it.properties
new file mode 100644
index 0000000..cc9b275
--- /dev/null
+++ b/syncope620/ext/camel/client-console/src/main/resources/org/apache/syncope/client/console/pages/CamelRouteModalPage_it.properties
@@ -0,0 +1,17 @@
+# 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.
+title=Rotta di Camel

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/ext/camel/client-console/src/main/resources/org/apache/syncope/client/console/pages/CamelRouteModalPage_pt_BR.properties
----------------------------------------------------------------------
diff --git a/syncope620/ext/camel/client-console/src/main/resources/org/apache/syncope/client/console/pages/CamelRouteModalPage_pt_BR.properties b/syncope620/ext/camel/client-console/src/main/resources/org/apache/syncope/client/console/pages/CamelRouteModalPage_pt_BR.properties
new file mode 100644
index 0000000..eceba57
--- /dev/null
+++ b/syncope620/ext/camel/client-console/src/main/resources/org/apache/syncope/client/console/pages/CamelRouteModalPage_pt_BR.properties
@@ -0,0 +1,17 @@
+# 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.
+title=Rota de Camel

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/ext/camel/client-console/src/main/resources/org/apache/syncope/client/console/panels/CamelRoutePanel.html
----------------------------------------------------------------------
diff --git a/syncope620/ext/camel/client-console/src/main/resources/org/apache/syncope/client/console/panels/CamelRoutePanel.html b/syncope620/ext/camel/client-console/src/main/resources/org/apache/syncope/client/console/panels/CamelRoutePanel.html
new file mode 100644
index 0000000..f1ba455
--- /dev/null
+++ b/syncope620/ext/camel/client-console/src/main/resources/org/apache/syncope/client/console/panels/CamelRoutePanel.html
@@ -0,0 +1,28 @@
+<!--
+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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:panel>
+    <div id="users-contain" class="ui-widget" style="width:inherit">
+      <span wicket:id="camelRoutesContainer">
+        <table class="ui-widget ui-widget-content table-hover" wicket:id="camelRouteTable"/>
+      </span>
+    </div>
+    <div wicket:id="editCamelRouteWin">[Show modal window for editing Camel route]</div>
+  </wicket:panel>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/ext/camel/pom.xml
----------------------------------------------------------------------
diff --git a/syncope620/ext/camel/pom.xml b/syncope620/ext/camel/pom.xml
index 0a95c94..3cee0b9 100644
--- a/syncope620/ext/camel/pom.xml
+++ b/syncope620/ext/camel/pom.xml
@@ -42,6 +42,7 @@ under the License.
     <module>logic</module>
     <module>rest-api</module>
     <module>rest-cxf</module>
+    <module>client-console</module>
   </modules>
 
 </project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba18d405/syncope620/fit/console-reference/pom.xml
----------------------------------------------------------------------
diff --git a/syncope620/fit/console-reference/pom.xml b/syncope620/fit/console-reference/pom.xml
index 6d94373..f893bb8 100644
--- a/syncope620/fit/console-reference/pom.xml
+++ b/syncope620/fit/console-reference/pom.xml
@@ -50,7 +50,12 @@ under the License.
       <groupId>javax.servlet</groupId>
       <artifactId>jstl</artifactId>
     </dependency>
-    
+
+    <dependency>
+      <groupId>org.apache.syncope.ext.camel</groupId>
+      <artifactId>syncope-ext-camel-client-console</artifactId>
+      <version>${project.version}</version>
+    </dependency>    
     <dependency>
       <groupId>org.apache.syncope.client</groupId>
       <artifactId>syncope-client-console</artifactId>
@@ -468,6 +473,9 @@ under the License.
                   <resource>
                     <directory>${basedir}/../../client/console/target/classes</directory>
                   </resource>
+                  <resource>
+                    <directory>${basedir}/../../ext/camel/client-console/target/classes</directory>
+                  </resource>
                 </resources>
               </classpath>
               
@@ -478,6 +486,10 @@ under the License.
                     <target>/</target>
                     <directory>${basedir}/../../client/console/target/classes/META-INF/resources/</directory>
                   </resource>
+                  <resource>
+                    <target>/</target>
+                    <directory>${basedir}/../../ext/camel/client-console/target/classes</directory>
+                  </resource>
                 </resources>
               </web>