You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by fm...@apache.org on 2015/10/23 12:29:24 UTC

[23/54] [abbrv] syncope git commit: introduced table view to print results (logger stack), SYNCOPE-158

introduced table view to print results (logger stack), SYNCOPE-158


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

Branch: refs/heads/SYNCOPE-156
Commit: 47392366e6f2cd04a8d016ef74f2bda408c86fe4
Parents: 2a99db9
Author: massi <ma...@tirasa.net>
Authored: Tue Oct 20 14:53:16 2015 +0200
Committer: massi <ma...@tirasa.net>
Committed: Tue Oct 20 14:53:16 2015 +0200

----------------------------------------------------------------------
 .../org/apache/syncope/client/cli/Input.java    |   4 +
 .../apache/syncope/client/cli/SyncopeAdm.java   |   8 +
 .../client/cli/commands/AbstractCommand.java    |  19 --
 .../cli/commands/ConfigurationCommand.java      | 124 ++++-----
 .../client/cli/commands/DomainCommand.java      |   1 +
 .../client/cli/commands/HelpCommand.java        |   4 +-
 .../client/cli/commands/InfoCommand.java        |   7 +-
 .../client/cli/commands/LoggerCommand.java      | 267 -------------------
 .../client/cli/commands/PolicyCommand.java      |   3 +-
 .../client/cli/commands/ReportCommand.java      |   3 +-
 .../client/cli/commands/SchemaCommand.java      |  16 +-
 .../client/cli/commands/TaskCommand.java        |   6 +-
 .../cli/commands/install/InstallCommand.java    |   2 +-
 .../commands/logger/AbstractLoggerCommand.java  |  30 +++
 .../cli/commands/logger/LoggerCommand.java      | 128 +++++++++
 .../cli/commands/logger/LoggerCreate.java       |  71 +++++
 .../cli/commands/logger/LoggerDelete.java       |  63 +++++
 .../client/cli/commands/logger/LoggerList.java  |  38 +++
 .../client/cli/commands/logger/LoggerRead.java  |  63 +++++
 .../cli/commands/logger/LoggerUpdate.java       |  78 ++++++
 .../cli/commands/logger/LoggerUpdateAll.java    |  69 +++++
 .../cli/commands/logger/ResultManager.java      |  60 +++++
 .../syncope/client/cli/messages/Messages.java   |  14 +-
 .../syncope/client/cli/messages/Table.java      | 190 +++++++++++++
 .../client/cli/messages/TwoColumnTable.java     | 262 ++++++++++++++++++
 .../syncope/client/cli/util/CommandUtils.java   |  20 +-
 26 files changed, 1172 insertions(+), 378 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/47392366/client/cli/src/main/java/org/apache/syncope/client/cli/Input.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/Input.java b/client/cli/src/main/java/org/apache/syncope/client/cli/Input.java
index 6e2aeeb..c53eefd 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/Input.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/Input.java
@@ -72,6 +72,10 @@ public class Input {
         return parameters[parameters.length - 1];
     }
 
+    public int parameterNumber() {
+        return parameters.length;
+    }
+
     public PairParameter toPairParameter(final String parameter) throws IllegalArgumentException {
         if (!parameter.contains("=")) {
             throw new IllegalArgumentException("Parameter syntax error!");

http://git-wip-us.apache.org/repos/asf/syncope/blob/47392366/client/cli/src/main/java/org/apache/syncope/client/cli/SyncopeAdm.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/SyncopeAdm.java b/client/cli/src/main/java/org/apache/syncope/client/cli/SyncopeAdm.java
index d503498..e62e818 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/SyncopeAdm.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/SyncopeAdm.java
@@ -35,6 +35,14 @@ public final class SyncopeAdm {
             ArgsManager.validator(args);
             final Input input = new Input(args);
             final AbstractCommand command = input.getCommand();
+
+            LOG.debug("Command: {}", command.getClass().getAnnotation(Command.class).name());
+            LOG.debug("Option: {}", input.getOption());
+            LOG.debug("Parameters:");
+            for (final String parameter : input.getParameters()) {
+                LOG.debug("   > " + parameter);
+            }
+
             command.execute(input);
         } catch (final IllegalAccessException | InstantiationException e) {
             System.out.println(helpMessage());

http://git-wip-us.apache.org/repos/asf/syncope/blob/47392366/client/cli/src/main/java/org/apache/syncope/client/cli/commands/AbstractCommand.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/AbstractCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/AbstractCommand.java
index 7cee202..6a03556 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/AbstractCommand.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/AbstractCommand.java
@@ -18,30 +18,11 @@
  */
 package org.apache.syncope.client.cli.commands;
 
-import java.util.List;
 import org.apache.syncope.client.cli.Input;
 
 public abstract class AbstractCommand {
 
     public abstract void execute(final Input input);
 
-    protected String helpMessage(final String command, final List<String> options) {
-        final StringBuilder helpMessageBuilder = new StringBuilder(String.format("Usage: %s [options]\n", command));
-        helpMessageBuilder.append("  Options:\n");
-        for (final String option : options) {
-            helpMessageBuilder.append("    ").append(option).append("\n");
-        }
-        return helpMessageBuilder.toString();
-    }
-
     public abstract String getHelpMessage();
-
-    protected static String[] fromEnumToArray(final Class<? extends Enum<?>> enumClass) {
-        final String[] types = new String[enumClass.getFields().length];
-        for (int i = 0; i < enumClass.getFields().length; i++) {
-            types[i] = enumClass.getFields()[i].getName();
-
-        }
-        return types;
-    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/47392366/client/cli/src/main/java/org/apache/syncope/client/cli/commands/ConfigurationCommand.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/ConfigurationCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/ConfigurationCommand.java
index ee225d7..01f6d23 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/ConfigurationCommand.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/ConfigurationCommand.java
@@ -22,7 +22,11 @@ import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.SequenceInputStream;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.transform.TransformerConfigurationException;
 import javax.xml.transform.TransformerException;
@@ -32,6 +36,7 @@ import org.apache.syncope.client.cli.Command;
 import org.apache.syncope.client.cli.Input;
 import org.apache.syncope.client.cli.SyncopeServices;
 import org.apache.syncope.client.cli.messages.Messages;
+import org.apache.syncope.client.cli.messages.TwoColumnTable;
 import org.apache.syncope.client.cli.util.XMLUtils;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.AttrTO;
@@ -51,13 +56,11 @@ public class ConfigurationCommand extends AbstractCommand {
     private static final String HELP_MESSAGE = "Usage: configuration [options]\n"
             + "  Options:\n"
             + "    --help \n"
-            + "    --list \n"
+            + "    --get \n"
             + "    --read \n"
             + "       Syntax: --read {CONF-NAME} {CONF-NAME} [...] \n"
             + "    --update \n"
             + "       Syntax: --update {CONF-NAME}={CONF-VALUE} {CONF-NAME}={CONF-VALUE} [...]\n"
-            + "    --create \n"
-            + "       Syntax: --create {CONF-NAME}={CONF-VALUE} {CONF-NAME}={CONF-VALUE} [...]\n"
             + "    --delete \n"
             + "       Syntax: --delete {CONF-NAME} {CONF-NAME} [...]\n"
             + "    --export \n"
@@ -80,32 +83,10 @@ public class ConfigurationCommand extends AbstractCommand {
 
         final ConfigurationService configurationService = SyncopeServices.get(ConfigurationService.class);
         switch (Options.fromName(input.getOption())) {
-            case LIST:
+            case GET:
                 try {
                     final ConfTO confTO = configurationService.list();
-                    System.out.println("\n - Configuration key: " + confTO.getKey());
-                    System.out.println("");
-                    System.out.println("Plain attributes");
-                    for (final AttrTO attrTO : confTO.getPlainAttrMap().values()) {
-                        System.out.println(" - Conf key: " + attrTO.getSchema());
-                        System.out.println("    - value(s): " + attrTO.getValues());
-                        System.out.println("    - readonly: " + attrTO.isReadonly());
-                    }
-                    System.out.println("");
-                    System.out.println("Derived attributes");
-                    for (final AttrTO attrTO : confTO.getDerAttrMap().values()) {
-                        System.out.println(" - Conf key: " + attrTO.getSchema());
-                        System.out.println("    - value(s): " + attrTO.getValues());
-                        System.out.println("    - readonly: " + attrTO.isReadonly());
-                    }
-                    System.out.println("");
-                    System.out.println("Virtual attributes");
-                    for (final AttrTO attrTO : confTO.getVirAttrMap().values()) {
-                        System.out.println(" - Conf key: " + attrTO.getSchema());
-                        System.out.println("    - value(s): " + attrTO.getValues());
-                        System.out.println("    - readonly: " + attrTO.isReadonly());
-                    }
-                    System.out.println("");
+                    toTable("Syncope configuration", "attribute", "value", confTO.getPlainAttrs());
                 } catch (final Exception ex) {
                     Messages.printMessage(ex.getMessage());
                     break;
@@ -114,23 +95,24 @@ public class ConfigurationCommand extends AbstractCommand {
             case READ:
                 final String readErrorMessage = "configuration --read {CONF-NAME} {CONF-NAME} [...]";
                 if (parameters.length >= 1) {
-                    AttrTO attrTO;
+                    final Set<AttrTO> attrList = new HashSet<>();
+                    boolean failed = false;
                     for (final String parameter : parameters) {
                         try {
-                            attrTO = configurationService.get(parameter);
-                            System.out.println("\n - Conf key: " + attrTO.getSchema());
-                            System.out.println("    - value(s): " + attrTO.getValues());
-                            System.out.println("    - readonly: " + attrTO.isReadonly());
-                            System.out.println("");
+                            attrList.add(configurationService.get(parameter));
                         } catch (final SyncopeClientException | WebServiceException ex) {
                             if (ex.getMessage().startsWith("NotFound")) {
                                 Messages.printNofFoundMessage("Logger", parameter);
                             } else {
                                 Messages.printMessage(ex.getMessage());
                             }
+                            failed = true;
                             break;
                         }
                     }
+                    if (!failed) {
+                        toTable("Read result", "attribute", "value", attrList);
+                    }
                 } else {
                     Messages.printCommandOptionMessage(readErrorMessage);
                 }
@@ -141,6 +123,8 @@ public class ConfigurationCommand extends AbstractCommand {
                 if (parameters.length >= 1) {
                     Input.PairParameter pairParameter = null;
                     AttrTO attrTO;
+                    final Set<AttrTO> attrList = new HashSet<>();
+                    boolean failed = false;
                     for (final String parameter : parameters) {
                         try {
                             pairParameter = input.toPairParameter(parameter);
@@ -148,12 +132,10 @@ public class ConfigurationCommand extends AbstractCommand {
                             attrTO.getValues().clear();
                             attrTO.getValues().add(pairParameter.getValue());
                             configurationService.set(attrTO);
-                            System.out.println("\n - Conf key " + attrTO.getSchema() + " updated. New value is:");
-                            System.out.println("    - value(s): " + attrTO.getValues());
-                            System.out.println("    - readonly: " + attrTO.isReadonly());
-                            System.out.println("");
+                            attrList.add(attrTO);
                         } catch (final IllegalArgumentException ex) {
                             Messages.printMessage(ex.getMessage(), updateErrorMessage);
+                            failed = true;
                             break;
                         } catch (final SyncopeClientException | WebServiceException ex) {
                             if (ex.getMessage().startsWith("NotFound")) {
@@ -165,45 +147,15 @@ public class ConfigurationCommand extends AbstractCommand {
                             } else {
                                 Messages.printMessage(ex.getMessage());
                             }
+                            failed = true;
                             break;
                         }
                     }
-                } else {
-                    Messages.printCommandOptionMessage(updateErrorMessage);
-                }
-                break;
-            case CREATE:
-                final String createErrorMessage
-                        = "configuration --create {CONF-NAME}={CONF-VALUE} {CONF-NAME}={CONF-VALUE} [...]";
-                if (parameters.length >= 1) {
-                    Input.PairParameter pairParameter = null;
-                    AttrTO attrTO;
-                    for (final String parameter : parameters) {
-                        try {
-                            pairParameter = input.toPairParameter(parameter);
-                            attrTO = new AttrTO();
-                            attrTO.setSchema(pairParameter.getKey());
-                            attrTO.getValues().add(pairParameter.getValue());
-                            configurationService.set(attrTO);
-                            System.out.println("\n - Conf key " + attrTO.getSchema() + " created. Value is:");
-                            System.out.println("    - value(s): " + attrTO.getValues());
-                            System.out.println("    - readonly: " + attrTO.isReadonly());
-                            System.out.println("");
-                        } catch (final IllegalArgumentException ex) {
-                            Messages.printMessage(ex.getMessage(), createErrorMessage);
-                            break;
-                        } catch (final SyncopeClientException | WebServiceException ex) {
-                            if (ex.getMessage().startsWith("NotFound")) {
-                                Messages.printNofFoundMessage("Configuration", pairParameter.getKey());
-                                System.out.println("Create it before.");
-                            } else {
-                                Messages.printMessage(ex.getMessage());
-                            }
-                            break;
-                        }
+                    if (!failed) {
+                        toTable("updated attribute", "attribute", "new value", attrList);
                     }
                 } else {
-                    Messages.printCommandOptionMessage(createErrorMessage);
+                    Messages.printCommandOptionMessage(updateErrorMessage);
                 }
                 break;
             case DELETE:
@@ -269,6 +221,33 @@ public class ConfigurationCommand extends AbstractCommand {
         }
     }
 
+    private void toTable(final String tableTitle,
+            final String firstHeader,
+            final String seconHeader,
+            final Set<AttrTO> attrList) {
+        int maxFirstColumnLenght = 0;
+        int maxSecondColumnLenght = 0;
+        final Map<String, String> attributes = new HashMap<>();
+        for (final AttrTO attrTO : attrList) {
+            String value = attrTO.getValues().toString();
+            value = value.substring(0, value.length() - 1);
+            value = value.substring(1, value.length());
+            attributes.put(attrTO.getSchema(), value);
+            if (attrTO.getSchema().length() > maxFirstColumnLenght) {
+                maxFirstColumnLenght = attrTO.getSchema().length();
+            }
+
+            if (value.length() > maxSecondColumnLenght) {
+                maxSecondColumnLenght = attrTO.getSchema().length();
+            }
+        }
+        final TwoColumnTable loggerTableResult = new TwoColumnTable(
+                tableTitle,
+                firstHeader, maxFirstColumnLenght,
+                seconHeader, maxSecondColumnLenght);
+        loggerTableResult.printTable(attributes);
+    }
+
     @Override
     public String getHelpMessage() {
         return HELP_MESSAGE;
@@ -277,10 +256,9 @@ public class ConfigurationCommand extends AbstractCommand {
     private enum Options {
 
         HELP("--help"),
-        LIST("--list"),
+        GET("--get"),
         READ("--read"),
         UPDATE("--update"),
-        CREATE("--create"),
         DELETE("--delete"),
         EXPORT("--export");
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/47392366/client/cli/src/main/java/org/apache/syncope/client/cli/commands/DomainCommand.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/DomainCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/DomainCommand.java
index a2a0977..e22359d 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/DomainCommand.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/DomainCommand.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.client.cli.commands;
 
+import org.apache.syncope.client.cli.commands.logger.LoggerCommand;
 import java.util.ArrayList;
 import java.util.List;
 import org.apache.commons.lang3.StringUtils;

http://git-wip-us.apache.org/repos/asf/syncope/blob/47392366/client/cli/src/main/java/org/apache/syncope/client/cli/commands/HelpCommand.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/HelpCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/HelpCommand.java
index 85b7d73..e415e14 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/HelpCommand.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/HelpCommand.java
@@ -63,14 +63,14 @@ public class HelpCommand extends AbstractCommand {
                 }
                 break;
             default:
-                Messages.printDefaultMessage(input.getOption(), helpMessage("info", Options.toList()));
+                Messages.printDefaultMessage(input.getOption(), CommandUtils.helpMessage("info", Options.toList()));
                 break;
         }
     }
 
     @Override
     public String getHelpMessage() {
-        return helpMessage("help", Options.toList());
+        return CommandUtils.helpMessage("help", Options.toList());
     }
 
     private enum Options {

http://git-wip-us.apache.org/repos/asf/syncope/blob/47392366/client/cli/src/main/java/org/apache/syncope/client/cli/commands/InfoCommand.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/InfoCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/InfoCommand.java
index 07bb952..1636c3d 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/InfoCommand.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/InfoCommand.java
@@ -25,6 +25,7 @@ import org.apache.syncope.client.cli.Command;
 import org.apache.syncope.client.cli.Input;
 import org.apache.syncope.client.cli.SyncopeServices;
 import org.apache.syncope.client.cli.messages.Messages;
+import org.apache.syncope.client.cli.util.CommandUtils;
 import org.apache.syncope.common.lib.to.SyncopeTO;
 import org.apache.syncope.common.rest.api.service.SyncopeService;
 import org.slf4j.Logger;
@@ -264,17 +265,17 @@ public class InfoCommand extends AbstractCommand {
                 }
                 break;
             case HELP:
-                System.out.println(helpMessage("info", Options.toList()));
+                System.out.println(CommandUtils.helpMessage("info", Options.toList()));
                 break;
             default:
-                Messages.printDefaultMessage(input.getOption(), helpMessage("info", Options.toList()));
+                Messages.printDefaultMessage(input.getOption(), CommandUtils.helpMessage("info", Options.toList()));
                 break;
         }
     }
 
     @Override
     public String getHelpMessage() {
-        return helpMessage("info", Options.toList());
+        return CommandUtils.helpMessage("info", Options.toList());
     }
 
     private enum Options {

http://git-wip-us.apache.org/repos/asf/syncope/blob/47392366/client/cli/src/main/java/org/apache/syncope/client/cli/commands/LoggerCommand.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/LoggerCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/LoggerCommand.java
deleted file mode 100644
index 6c11c6f..0000000
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/LoggerCommand.java
+++ /dev/null
@@ -1,267 +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.cli.commands;
-
-import java.util.ArrayList;
-import java.util.List;
-import javax.xml.ws.WebServiceException;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.client.cli.Command;
-import org.apache.syncope.client.cli.Input;
-import org.apache.syncope.client.cli.SyncopeServices;
-import org.apache.syncope.client.cli.messages.Messages;
-import org.apache.syncope.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.to.LoggerTO;
-import org.apache.syncope.common.lib.types.LoggerLevel;
-import org.apache.syncope.common.lib.types.LoggerType;
-import org.apache.syncope.common.rest.api.service.LoggerService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-@Command(name = "logger")
-public class LoggerCommand extends AbstractCommand {
-
-    private static final Logger LOG = LoggerFactory.getLogger(LoggerCommand.class);
-
-    private static final String HELP_MESSAGE = "Usage: logger [options]\n"
-            + "  Options:\n"
-            + "    --help \n"
-            + "    --list \n"
-            + "    --read \n"
-            + "       Syntax: --read {LOG-NAME} {LOG-NAME} [...]\n"
-            + "    --update \n"
-            + "       Syntax: --update {LOG-NAME}={LOG-LEVEL} {LOG-NAME}={LOG-LEVEL} [...]\n"
-            + "    --update-all \n"
-            + "       Syntax: --update-all {LOG-LEVEL} \n"
-            + "    --create \n"
-            + "       Syntax: --create {LOG-NAME}={LOG-LEVEL} {LOG-NAME}={LOG-LEVEL} [...]\n"
-            + "    --delete \n"
-            + "       Syntax: --delete {LOG-NAME} {LOG-NAME} [...]";
-
-    @Override
-    public void execute(final Input input) {
-        LOG.debug("Option: {}", input.getOption());
-        LOG.debug("Parameters:");
-        for (final String parameter : input.getParameters()) {
-            LOG.debug("   > " + parameter);
-        }
-
-        final String[] parameters = input.getParameters();
-
-        if (StringUtils.isBlank(input.getOption())) {
-            input.setOption(Options.HELP.getOptionName());
-        }
-
-        final LoggerService loggerService = SyncopeServices.get(LoggerService.class);
-        switch (Options.fromName(input.getOption())) {
-            case LIST:
-                try {
-                    for (final LoggerTO loggerTO : loggerService.list(LoggerType.LOG)) {
-                        System.out.println(" - " + loggerTO.getKey() + " -> " + loggerTO.getLevel());
-                        System.out.println("");
-                    }
-                } catch (final SyncopeClientException ex) {
-                    Messages.printMessage("Error: " + ex.getMessage());
-                }
-                break;
-            case READ:
-                final String readErrorMessage = "logger --read {LOG-NAME} {LOG-NAME} [...]";
-                if (parameters.length >= 1) {
-                    for (final String parameter : parameters) {
-                        try {
-                            final LoggerTO loggerTO = loggerService.read(LoggerType.LOG, parameter);
-                            System.out.println("\n - Logger");
-                            System.out.println("   - key: " + loggerTO.getKey());
-                            System.out.println("   - level: " + loggerTO.getLevel());
-                            System.out.println("");
-                        } catch (final SyncopeClientException | WebServiceException ex) {
-                            if (ex.getMessage().startsWith("NotFound")) {
-                                Messages.printNofFoundMessage("Logger", parameter);
-                            } else {
-                                Messages.printMessage("Error: " + ex.getMessage());
-                            }
-                        }
-                    }
-                } else {
-                    Messages.printCommandOptionMessage(readErrorMessage);
-                }
-                break;
-            case UPDATE:
-                final String updateErrorMessage = "logger --update {LOG-NAME}={LOG-LEVEL} {LOG-NAME}={LOG-LEVEL} [...]";
-
-                if (parameters.length >= 1) {
-                    Input.PairParameter pairParameter;
-                    for (final String parameter : parameters) {
-                        try {
-                            pairParameter = input.toPairParameter(parameter);
-                            final LoggerTO loggerTO = loggerService.read(LoggerType.LOG, pairParameter.getKey());
-                            loggerTO.setLevel(LoggerLevel.valueOf(pairParameter.getValue()));
-                            loggerService.update(LoggerType.LOG, loggerTO);
-                            System.out.
-                                    println("\n - Logger " + loggerTO.getKey() + " updated");
-                            System.out.println("   - new level: " + loggerTO.getLevel());
-                            System.out.println("");
-                        } catch (final WebServiceException | SyncopeClientException | IllegalArgumentException ex) {
-                            if (ex.getMessage().startsWith("No enum constant org.apache.syncope.common.lib.types.")) {
-                                Messages.printTypeNotValidMessage(
-                                        "logger level", input.firstParameter(), fromEnumToArray(LoggerLevel.class));
-                            } else if ("Parameter syntax error!".equalsIgnoreCase(ex.getMessage())) {
-                                Messages.printMessage(ex.getMessage(), updateErrorMessage);
-                            } else if (ex.getMessage().startsWith("NotFound")) {
-                                Messages.printNofFoundMessage("Logger", parameter);
-                            } else {
-                                Messages.printMessage(ex.getMessage(), updateErrorMessage);
-                            }
-                            break;
-                        }
-                    }
-                } else {
-                    Messages.printCommandOptionMessage(updateErrorMessage);
-                }
-                break;
-            case UPDATE_ALL:
-                final String updateAllErrorMessage = "logger --update-all {LOG-LEVEL}";
-
-                if (parameters.length == 1) {
-                    for (final LoggerTO loggerTO : loggerService.list(LoggerType.LOG)) {
-                        try {
-                            loggerTO.setLevel(LoggerLevel.valueOf(parameters[0]));
-                            loggerService.update(LoggerType.LOG, loggerTO);
-                            System.out.
-                                    println("\n - Logger " + loggerTO.getKey() + " updated");
-                            System.out.println("   - new level: " + loggerTO.getLevel());
-                            System.out.println("");
-                        } catch (final WebServiceException | SyncopeClientException | IllegalArgumentException ex) {
-                            if (ex.getMessage().startsWith("No enum constant org.apache.syncope.common.lib.types.")) {
-                                Messages.printTypeNotValidMessage(
-                                        "logger level", input.firstParameter(), fromEnumToArray(LoggerLevel.class));
-                            } else {
-                                Messages.printMessage(ex.getMessage(), updateAllErrorMessage);
-                            }
-                            break;
-                        }
-                    }
-                } else {
-                    Messages.printCommandOptionMessage(updateAllErrorMessage);
-                }
-                break;
-            case CREATE:
-                final String createErrorMessage = "logger --create {LOG-NAME}={LOG-LEVEL} {LOG-NAME}={LOG-LEVEL} [...]";
-
-                if (parameters.length >= 1) {
-                    Input.PairParameter pairParameter;
-                    LoggerTO loggerTO;
-                    for (final String parameter : parameters) {
-                        loggerTO = new LoggerTO();
-                        try {
-                            pairParameter = input.toPairParameter(parameter);
-                            loggerTO.setKey(pairParameter.getKey());
-                            loggerTO.setLevel(LoggerLevel.valueOf(pairParameter.getValue()));
-                            loggerService.update(LoggerType.LOG, loggerTO);
-                            System.out.
-                                    println("\n - Logger " + loggerTO.getKey() + " updated");
-                            System.out.println("   - level: " + loggerTO.getLevel());
-                            System.out.println("");
-                        } catch (final WebServiceException | SyncopeClientException | IllegalArgumentException ex) {
-                            Messages.printTypeNotValidMessage(
-                                    "logger level", input.firstParameter(), fromEnumToArray(LoggerLevel.class));
-                            break;
-                        }
-                    }
-                } else {
-                    Messages.printCommandOptionMessage(createErrorMessage);
-                }
-                break;
-            case DELETE:
-                final String deleteErrorMessage = "logger --delete {LOG-NAME} {LOG-NAME} [...]";
-
-                if (parameters.length >= 1) {
-                    for (final String parameter : parameters) {
-                        try {
-                            loggerService.delete(LoggerType.LOG, parameter);
-                            Messages.printDeletedMessage("Logger", parameter);
-                        } catch (final WebServiceException | SyncopeClientException ex) {
-                            if (ex.getMessage().startsWith("NotFound")) {
-                                Messages.printNofFoundMessage("Logger", parameter);
-                            } else {
-                                Messages.printMessage(ex.getMessage());
-                            }
-                        }
-                    }
-                } else {
-                    Messages.printCommandOptionMessage(deleteErrorMessage);
-                }
-                break;
-            case HELP:
-                System.out.println(HELP_MESSAGE);
-                break;
-            default:
-                Messages.printDefaultMessage(input.getOption(), HELP_MESSAGE);
-        }
-    }
-
-    @Override
-    public String getHelpMessage() {
-        return HELP_MESSAGE;
-    }
-
-    private enum Options {
-
-        HELP("--help"),
-        LIST("--list"),
-        READ("--read"),
-        UPDATE("--update"),
-        UPDATE_ALL("--update-all"),
-        CREATE("--create"),
-        DELETE("--delete");
-
-        private final String optionName;
-
-        private Options(final String optionName) {
-            this.optionName = optionName;
-        }
-
-        public String getOptionName() {
-            return optionName;
-        }
-
-        public boolean equalsOptionName(final String otherName) {
-            return (otherName == null) ? false : optionName.equals(otherName);
-        }
-
-        public static Options fromName(final String name) {
-            Options optionToReturn = HELP;
-            for (final Options option : Options.values()) {
-                if (option.equalsOptionName(name)) {
-                    optionToReturn = option;
-                }
-            }
-            return optionToReturn;
-        }
-
-        public static List<String> toList() {
-            final List<String> options = new ArrayList<>();
-            for (final Options value : values()) {
-                options.add(value.getOptionName());
-            }
-            return options;
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/47392366/client/cli/src/main/java/org/apache/syncope/client/cli/commands/PolicyCommand.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/PolicyCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/PolicyCommand.java
index a69be18..28c528e 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/PolicyCommand.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/PolicyCommand.java
@@ -26,6 +26,7 @@ import org.apache.syncope.client.cli.Command;
 import org.apache.syncope.client.cli.Input;
 import org.apache.syncope.client.cli.SyncopeServices;
 import org.apache.syncope.client.cli.messages.Messages;
+import org.apache.syncope.client.cli.util.CommandUtils;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.policy.AbstractPolicyTO;
 import org.apache.syncope.common.lib.types.PolicyType;
@@ -78,7 +79,7 @@ public class PolicyCommand extends AbstractCommand {
                         Messages.printMessage(ex.getMessage());
                     } catch (final IllegalArgumentException ex) {
                         Messages.printTypeNotValidMessage(
-                                "policy", input.firstParameter(), fromEnumToArray(PolicyType.class));
+                                "policy", input.firstParameter(), CommandUtils.fromEnumToArray(PolicyType.class));
                     }
                 } else {
                     Messages.printCommandOptionMessage(listPolicyErrorMessage);

http://git-wip-us.apache.org/repos/asf/syncope/blob/47392366/client/cli/src/main/java/org/apache/syncope/client/cli/commands/ReportCommand.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/ReportCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/ReportCommand.java
index 2178d22..935e4f1 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/ReportCommand.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/ReportCommand.java
@@ -31,6 +31,7 @@ import org.apache.syncope.client.cli.Command;
 import org.apache.syncope.client.cli.Input;
 import org.apache.syncope.client.cli.SyncopeServices;
 import org.apache.syncope.client.cli.messages.Messages;
+import org.apache.syncope.client.cli.util.CommandUtils;
 import org.apache.syncope.client.cli.util.XMLUtils;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.ReportExecTO;
@@ -283,7 +284,7 @@ public class ReportCommand extends AbstractCommand {
                         } catch (final IllegalArgumentException ex) {
                             Messages.printTypeNotValidMessage(
                                     "format", input.firstParameter(),
-                                    fromEnumToArray(ReportExecExportFormat.class));
+                                    CommandUtils.fromEnumToArray(ReportExecExportFormat.class));
                         }
                         break;
                     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/47392366/client/cli/src/main/java/org/apache/syncope/client/cli/commands/SchemaCommand.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/SchemaCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/SchemaCommand.java
index 7cb9ca1..bd3ffff 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/SchemaCommand.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/SchemaCommand.java
@@ -18,8 +18,6 @@
  */
 package org.apache.syncope.client.cli.commands;
 
-import static org.apache.syncope.client.cli.commands.AbstractCommand.fromEnumToArray;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -29,6 +27,7 @@ import org.apache.syncope.client.cli.Command;
 import org.apache.syncope.client.cli.Input;
 import org.apache.syncope.client.cli.SyncopeServices;
 import org.apache.syncope.client.cli.messages.Messages;
+import org.apache.syncope.client.cli.util.CommandUtils;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.AbstractSchemaTO;
 import org.apache.syncope.common.lib.to.DerSchemaTO;
@@ -51,10 +50,13 @@ public class SchemaCommand extends AbstractCommand {
             + "    --list-plain\n"
             + "    --list-derived\n"
             + "    --list-virtual\n"
-            + "    --list {SCHEMA-TYPE}\n"
-            + "       Schema type: PLAIN / DERIVED / VIRTUAL";
+            + "    --read {SCHEMA-TYPE} {SCHEMA-KEY}\n"
+            + "        Schema type: PLAIN / DERIVED / VIRTUAL\n"
+            + "    --delete {SCHEMA-TYPE} {SCHEMA-KEY}\n"
+            + "        Schema type: PLAIN / DERIVED / VIRTUAL";
 
     @Override
+
     public void execute(final Input input) {
         LOG.debug("Option: {}", input.getOption());
         LOG.debug("Parameters:");
@@ -101,7 +103,7 @@ public class SchemaCommand extends AbstractCommand {
                         Messages.printMessage(ex.getMessage());
                     } catch (final IllegalArgumentException ex) {
                         Messages.printTypeNotValidMessage(
-                                "schema", input.firstParameter(), fromEnumToArray(SchemaType.class));
+                                "schema", input.firstParameter(), CommandUtils.fromEnumToArray(SchemaType.class));
                     }
                 } else {
                     Messages.printCommandOptionMessage(listErrorMessage);
@@ -216,7 +218,7 @@ public class SchemaCommand extends AbstractCommand {
                         }
                     } catch (final IllegalArgumentException ex) {
                         Messages.printTypeNotValidMessage(
-                                "schema", input.firstParameter(), fromEnumToArray(SchemaType.class));
+                                "schema", input.firstParameter(), CommandUtils.fromEnumToArray(SchemaType.class));
                     }
                 } else {
                     Messages.printCommandOptionMessage(readErrorMessage);
@@ -243,7 +245,7 @@ public class SchemaCommand extends AbstractCommand {
                         }
                     } catch (final IllegalArgumentException ex) {
                         Messages.printTypeNotValidMessage(
-                                "schema", input.firstParameter(), fromEnumToArray(SchemaType.class));
+                                "schema", input.firstParameter(), CommandUtils.fromEnumToArray(SchemaType.class));
                     }
                 } else {
                     Messages.printCommandOptionMessage(deleteErrorMessage);

http://git-wip-us.apache.org/repos/asf/syncope/blob/47392366/client/cli/src/main/java/org/apache/syncope/client/cli/commands/TaskCommand.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/TaskCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/TaskCommand.java
index 9dcb795..5828823 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/TaskCommand.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/TaskCommand.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.client.cli.commands;
 
+import org.apache.syncope.client.cli.commands.logger.LoggerCommand;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -27,6 +28,7 @@ import org.apache.syncope.client.cli.Command;
 import org.apache.syncope.client.cli.Input;
 import org.apache.syncope.client.cli.SyncopeServices;
 import org.apache.syncope.client.cli.messages.Messages;
+import org.apache.syncope.client.cli.util.CommandUtils;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.AbstractTaskTO;
 import org.apache.syncope.common.lib.to.AnyTO;
@@ -230,7 +232,8 @@ public class TaskCommand extends AbstractCommand {
                     } catch (final SyncopeClientException ex) {
                         Messages.printMessage(ex.getMessage());
                     } catch (final IllegalArgumentException ex) {
-                        Messages.printTypeNotValidMessage("task", parameters[0], fromEnumToArray(TaskType.class));
+                        Messages.printTypeNotValidMessage(
+                                "task", parameters[0], CommandUtils.fromEnumToArray(TaskType.class));
                     }
                 } else {
                     Messages.printCommandOptionMessage(listTaskErrorMessage);
@@ -493,7 +496,6 @@ public class TaskCommand extends AbstractCommand {
                         final TaskExecTO taskExecTO = taskService.execute(taskIdToExecute, dryRun);
                         printTaskExecTO(taskExecTO);
                     } catch (final WebServiceException | SyncopeClientException ex) {
-                        System.out.println("Error:");
                         if (ex.getMessage().startsWith("NotFound")) {
                             Messages.printNofFoundMessage("Task", parameters[0]);
                         } else if (ex.getMessage().startsWith("DataIntegrityViolation")) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/47392366/client/cli/src/main/java/org/apache/syncope/client/cli/commands/install/InstallCommand.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/install/InstallCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/install/InstallCommand.java
index e049a28..0092210 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/install/InstallCommand.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/install/InstallCommand.java
@@ -29,7 +29,7 @@ import org.apache.syncope.client.cli.Command;
 import org.apache.syncope.client.cli.Input;
 import org.apache.syncope.client.cli.SyncopeServices;
 import org.apache.syncope.client.cli.commands.AbstractCommand;
-import org.apache.syncope.client.cli.commands.LoggerCommand;
+import org.apache.syncope.client.cli.commands.logger.LoggerCommand;
 import org.apache.syncope.client.cli.util.FileSystemUtils;
 import org.apache.syncope.client.cli.util.JasyptUtils;
 import org.apache.syncope.common.rest.api.service.SyncopeService;

http://git-wip-us.apache.org/repos/asf/syncope/blob/47392366/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/AbstractLoggerCommand.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/AbstractLoggerCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/AbstractLoggerCommand.java
new file mode 100644
index 0000000..4654cb6
--- /dev/null
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/AbstractLoggerCommand.java
@@ -0,0 +1,30 @@
+/*
+ * 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.cli.commands.logger;
+
+import org.apache.syncope.client.cli.SyncopeServices;
+import org.apache.syncope.common.rest.api.service.LoggerService;
+
+public class AbstractLoggerCommand {
+
+    protected final LoggerService loggerService = SyncopeServices.get(LoggerService.class);
+    
+    protected final ResultManager resultManager = new ResultManager();
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/47392366/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerCommand.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerCommand.java
new file mode 100644
index 0000000..1199218
--- /dev/null
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerCommand.java
@@ -0,0 +1,128 @@
+/*
+ * 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.cli.commands.logger;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.cli.Command;
+import org.apache.syncope.client.cli.Input;
+import org.apache.syncope.client.cli.commands.AbstractCommand;
+import org.apache.syncope.client.cli.messages.Messages;
+
+@Command(name = "logger")
+public class LoggerCommand extends AbstractCommand {
+
+    private static final String HELP_MESSAGE = "Usage: logger [options]\n"
+            + "  Options:\n"
+            + "    --help \n"
+            + "    --list \n"
+            + "    --read \n"
+            + "       Syntax: --read {LOG-NAME} {LOG-NAME} [...]\n"
+            + "    --update \n"
+            + "       Syntax: --update {LOG-NAME}={LOG-LEVEL} {LOG-NAME}={LOG-LEVEL} [...]\n"
+            + "    --update-all \n"
+            + "       Syntax: --update-all {LOG-LEVEL} \n"
+            + "    --create \n"
+            + "       Syntax: --create {LOG-NAME}={LOG-LEVEL} {LOG-NAME}={LOG-LEVEL} [...]\n"
+            + "    --delete \n"
+            + "       Syntax: --delete {LOG-NAME} {LOG-NAME} [...]";
+
+    @Override
+    public void execute(final Input input) {
+        if (StringUtils.isBlank(input.getOption())) {
+            input.setOption(LoggerOptions.HELP.getOptionName());
+        }
+
+        switch (LoggerOptions.fromName(input.getOption())) {
+            case LIST:
+                new LoggerList().list();
+                break;
+            case READ:
+                new LoggerRead(input).read();
+                break;
+            case UPDATE:
+                new LoggerUpdate(input).update();
+                break;
+            case UPDATE_ALL:
+                new LoggerUpdateAll(input).updateAll();
+                break;
+            case CREATE:
+                new LoggerCreate(input).create();
+                break;
+            case DELETE:
+                new LoggerDelete(input).delete();
+                break;
+            case HELP:
+                System.out.println(HELP_MESSAGE);
+                break;
+            default:
+                Messages.printDefaultMessage(input.getOption(), HELP_MESSAGE);
+        }
+    }
+
+    @Override
+    public String getHelpMessage() {
+        return HELP_MESSAGE;
+    }
+
+    private enum LoggerOptions {
+
+        HELP("--help"),
+        LIST("--list"),
+        READ("--read"),
+        UPDATE("--update"),
+        UPDATE_ALL("--update-all"),
+        CREATE("--create"),
+        DELETE("--delete");
+
+        private final String optionName;
+
+        private LoggerOptions(final String optionName) {
+            this.optionName = optionName;
+        }
+
+        public String getOptionName() {
+            return optionName;
+        }
+
+        public boolean equalsOptionName(final String otherName) {
+            return (otherName == null) ? false : optionName.equals(otherName);
+        }
+
+        public static LoggerOptions fromName(final String name) {
+            LoggerOptions optionToReturn = HELP;
+            for (final LoggerOptions option : LoggerOptions.values()) {
+                if (option.equalsOptionName(name)) {
+                    optionToReturn = option;
+                }
+            }
+            return optionToReturn;
+        }
+
+        public static List<String> toList() {
+            final List<String> options = new ArrayList<>();
+            for (final LoggerOptions value : values()) {
+                options.add(value.getOptionName());
+            }
+            return options;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/47392366/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerCreate.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerCreate.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerCreate.java
new file mode 100644
index 0000000..1444938
--- /dev/null
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerCreate.java
@@ -0,0 +1,71 @@
+/*
+ * 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.cli.commands.logger;
+
+import java.util.LinkedList;
+import javax.xml.ws.WebServiceException;
+import org.apache.syncope.client.cli.Input;
+import org.apache.syncope.client.cli.messages.Messages;
+import org.apache.syncope.client.cli.util.CommandUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.LoggerTO;
+import org.apache.syncope.common.lib.types.LoggerLevel;
+import org.apache.syncope.common.lib.types.LoggerType;
+
+public class LoggerCreate extends AbstractLoggerCommand {
+
+    private static final String CREATE_HELP_MESSAGE
+            = "logger --create {LOG-NAME}={LOG-LEVEL} {LOG-NAME}={LOG-LEVEL} [...]";
+
+    private final Input input;
+
+    public LoggerCreate(final Input input) {
+        this.input = input;
+    }
+
+    public void create() {
+        if (input.parameterNumber() >= 1) {
+            Input.PairParameter pairParameter;
+            LoggerTO loggerTO;
+            final LinkedList<LoggerTO> loggerTOs = new LinkedList<>();
+            boolean failed = false;
+            for (final String parameter : input.getParameters()) {
+                loggerTO = new LoggerTO();
+                try {
+                    pairParameter = input.toPairParameter(parameter);
+                    loggerTO.setKey(pairParameter.getKey());
+                    loggerTO.setLevel(LoggerLevel.valueOf(pairParameter.getValue()));
+                    loggerService.update(LoggerType.LOG, loggerTO);
+                    loggerTOs.add(loggerTO);
+                } catch (final WebServiceException | SyncopeClientException | IllegalArgumentException ex) {
+                    Messages.printTypeNotValidMessage(
+                            "logger level", input.firstParameter(), CommandUtils.fromEnumToArray(LoggerLevel.class));
+                    failed = true;
+                    break;
+                }
+            }
+            if (!failed) {
+                resultManager.fromUpdate(loggerTOs);
+            }
+        } else {
+            Messages.printCommandOptionMessage(CREATE_HELP_MESSAGE);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/47392366/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerDelete.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerDelete.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerDelete.java
new file mode 100644
index 0000000..49becd5
--- /dev/null
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerDelete.java
@@ -0,0 +1,63 @@
+/*
+ * 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.cli.commands.logger;
+
+import java.util.LinkedList;
+import javax.xml.ws.WebServiceException;
+import org.apache.syncope.client.cli.Input;
+import org.apache.syncope.client.cli.messages.Messages;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.LoggerTO;
+import org.apache.syncope.common.lib.types.LoggerType;
+
+public class LoggerDelete extends AbstractLoggerCommand {
+
+    private static final String DELETE_HELP_MESSAGE = "logger --delete {LOG-NAME} {LOG-NAME} [...]";
+
+    private final Input input;
+
+    public LoggerDelete(final Input input) {
+        this.input = input;
+    }
+
+    public void delete() {
+        if (input.parameterNumber() >= 1) {
+            final LinkedList<LoggerTO> loggerTOs = new LinkedList<>();
+            boolean failed = false;
+            for (final String parameter : input.getParameters()) {
+                try {
+                    loggerService.delete(LoggerType.LOG, parameter);
+                } catch (final WebServiceException | SyncopeClientException ex) {
+                    if (ex.getMessage().startsWith("NotFound")) {
+                        Messages.printNofFoundMessage("Logger", parameter);
+                    } else {
+                        Messages.printMessage(ex.getMessage());
+                    }
+                    failed = true;
+                }
+            }
+            if (!failed) {
+                resultManager.fromDelete(loggerTOs);
+            }
+        } else {
+            Messages.printCommandOptionMessage(DELETE_HELP_MESSAGE);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/47392366/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerList.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerList.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerList.java
new file mode 100644
index 0000000..78d2ced
--- /dev/null
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerList.java
@@ -0,0 +1,38 @@
+/*
+ * 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.cli.commands.logger;
+
+import java.util.LinkedList;
+import org.apache.syncope.client.cli.messages.Messages;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.types.LoggerType;
+
+public class LoggerList extends AbstractLoggerCommand {
+
+    public LoggerList() {
+    }
+
+    public void list() {
+        try {
+            resultManager.fromList(new LinkedList<>(loggerService.list(LoggerType.LOG)));
+        } catch (final SyncopeClientException ex) {
+            Messages.printMessage("Error: " + ex.getMessage());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/47392366/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerRead.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerRead.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerRead.java
new file mode 100644
index 0000000..4d33191
--- /dev/null
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerRead.java
@@ -0,0 +1,63 @@
+/*
+ * 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.cli.commands.logger;
+
+import java.util.LinkedList;
+import javax.xml.ws.WebServiceException;
+import org.apache.syncope.client.cli.Input;
+import org.apache.syncope.client.cli.messages.Messages;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.LoggerTO;
+import org.apache.syncope.common.lib.types.LoggerType;
+
+public class LoggerRead extends AbstractLoggerCommand {
+
+    private static final String READ_HELP_MESSAGE = "logger --read {LOG-NAME} {LOG-NAME} [...]";
+
+    private final Input input;
+
+    public LoggerRead(final Input input) {
+        this.input = input;
+    }
+
+    public void read() {
+        if (input.getParameters().length >= 1) {
+            final LinkedList<LoggerTO> loggerTOs = new LinkedList<>();
+            boolean failed = false;
+            for (final String parameter : input.getParameters()) {
+                try {
+                    loggerTOs.add(loggerService.read(LoggerType.LOG, parameter));
+                } catch (final SyncopeClientException | WebServiceException ex) {
+                    if (ex.getMessage().startsWith("NotFound")) {
+                        Messages.printNofFoundMessage("Logger", parameter);
+                    } else {
+                        Messages.printMessage("Error: " + ex.getMessage());
+                    }
+                    failed = true;
+                    break;
+                }
+            }
+            if (!failed) {
+                resultManager.fromRead(loggerTOs);
+            }
+        } else {
+            Messages.printCommandOptionMessage(READ_HELP_MESSAGE);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/47392366/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerUpdate.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerUpdate.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerUpdate.java
new file mode 100644
index 0000000..fccfe72
--- /dev/null
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerUpdate.java
@@ -0,0 +1,78 @@
+/*
+ * 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.cli.commands.logger;
+
+import java.util.LinkedList;
+import javax.xml.ws.WebServiceException;
+import org.apache.syncope.client.cli.Input;
+import org.apache.syncope.client.cli.messages.Messages;
+import org.apache.syncope.client.cli.util.CommandUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.LoggerTO;
+import org.apache.syncope.common.lib.types.LoggerLevel;
+import org.apache.syncope.common.lib.types.LoggerType;
+
+public class LoggerUpdate extends AbstractLoggerCommand {
+
+    private static final String UPDATE_HELP_MESSAGE
+            = "logger --update {LOG-NAME}={LOG-LEVEL} {LOG-NAME}={LOG-LEVEL} [...]";
+
+    private final Input input;
+
+    public LoggerUpdate(final Input input) {
+        this.input = input;
+    }
+
+    public void update() {
+        if (input.parameterNumber() >= 1) {
+            Input.PairParameter pairParameter;
+            LoggerTO loggerTO;
+            final LinkedList<LoggerTO> loggerTOs = new LinkedList<>();
+            boolean failed = false;
+            for (final String parameter : input.getParameters()) {
+                try {
+                    pairParameter = input.toPairParameter(parameter);
+                    loggerTO = loggerService.read(LoggerType.LOG, pairParameter.getKey());
+                    loggerTO.setLevel(LoggerLevel.valueOf(pairParameter.getValue()));
+                    loggerService.update(LoggerType.LOG, loggerTO);
+                    loggerTOs.add(loggerTO);
+                } catch (final WebServiceException | SyncopeClientException | IllegalArgumentException ex) {
+                    if (ex.getMessage().startsWith("No enum constant org.apache.syncope.common.lib.types.")) {
+                        Messages.printTypeNotValidMessage(
+                                "logger level",
+                                input.firstParameter(), CommandUtils.fromEnumToArray(LoggerLevel.class));
+                    } else if ("Parameter syntax error!".equalsIgnoreCase(ex.getMessage())) {
+                        Messages.printMessage(ex.getMessage(), UPDATE_HELP_MESSAGE);
+                    } else if (ex.getMessage().startsWith("NotFound")) {
+                        Messages.printNofFoundMessage("Logger", parameter);
+                    } else {
+                        Messages.printMessage(ex.getMessage(), UPDATE_HELP_MESSAGE);
+                    }
+                    failed = true;
+                    break;
+                }
+            }
+            if (!failed) {
+                resultManager.fromUpdate(loggerTOs);
+            }
+        } else {
+            Messages.printCommandOptionMessage(UPDATE_HELP_MESSAGE);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/47392366/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerUpdateAll.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerUpdateAll.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerUpdateAll.java
new file mode 100644
index 0000000..b0ee25c
--- /dev/null
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerUpdateAll.java
@@ -0,0 +1,69 @@
+/*
+ * 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.cli.commands.logger;
+
+import java.util.LinkedList;
+import javax.xml.ws.WebServiceException;
+import org.apache.syncope.client.cli.Input;
+import org.apache.syncope.client.cli.messages.Messages;
+import org.apache.syncope.client.cli.util.CommandUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.LoggerTO;
+import org.apache.syncope.common.lib.types.LoggerLevel;
+import org.apache.syncope.common.lib.types.LoggerType;
+
+public class LoggerUpdateAll extends AbstractLoggerCommand {
+
+    private static final String UPDATE_ALL_HELP_MESSAGE = "logger --update-all {LOG-LEVEL}";
+
+    private final Input input;
+
+    public LoggerUpdateAll(final Input input) {
+        this.input = input;
+    }
+
+    public void updateAll() {
+        if (input.parameterNumber() == 1) {
+            final LinkedList<LoggerTO> loggerTOs = new LinkedList<>();
+            boolean failed = false;
+            for (final LoggerTO loggerTO : loggerService.list(LoggerType.LOG)) {
+                try {
+                    loggerTO.setLevel(LoggerLevel.valueOf(input.firstParameter()));
+                    loggerService.update(LoggerType.LOG, loggerTO);
+                    loggerTOs.add(loggerTO);
+                } catch (final WebServiceException | SyncopeClientException | IllegalArgumentException ex) {
+                    if (ex.getMessage().startsWith("No enum constant org.apache.syncope.common.lib.types.")) {
+                        Messages.printTypeNotValidMessage(
+                                "logger level",
+                                input.firstParameter(), CommandUtils.fromEnumToArray(LoggerLevel.class));
+                    } else {
+                        Messages.printMessage(ex.getMessage(), UPDATE_ALL_HELP_MESSAGE);
+                    }
+                    failed = true;
+                    break;
+                }
+            }
+            if (!failed) {
+                resultManager.fromUpdate(loggerTOs);
+            }
+        } else {
+            Messages.printCommandOptionMessage(UPDATE_ALL_HELP_MESSAGE);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/47392366/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/ResultManager.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/ResultManager.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/ResultManager.java
new file mode 100644
index 0000000..102ff74
--- /dev/null
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/ResultManager.java
@@ -0,0 +1,60 @@
+/*
+ * 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.cli.commands.logger;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+import org.apache.syncope.client.cli.messages.Table;
+import org.apache.syncope.common.lib.to.LoggerTO;
+
+public class ResultManager {
+
+    public void fromList(final LinkedList<LoggerTO> loggerTOs) {
+        fromCommandToView("list loggers", "level", loggerTOs);
+    }
+
+    public void fromRead(final LinkedList<LoggerTO> loggerTOs) {
+        fromCommandToView("read loggers", "level", loggerTOs);
+    }
+
+    public void fromCreate(final LinkedList<LoggerTO> loggerTOs) {
+        fromCommandToView("created loggers", "level", loggerTOs);
+    }
+
+    public void fromUpdate(final LinkedList<LoggerTO> loggerTOs) {
+        fromCommandToView("updated loggers", "new level", loggerTOs);
+    }
+
+    public void fromDelete(final LinkedList<LoggerTO> loggerTOs) {
+        fromCommandToView("deleted loggers", "new level", loggerTOs);
+    }
+
+    private void fromCommandToView(final String title,
+            final String secondHeader,
+            final LinkedList<LoggerTO> loggerTOs) {
+        final Table.TableBuilder tableBuilder
+                = new Table.TableBuilder(title).header("logger").header(secondHeader);
+        for (final LoggerTO loggerTO : loggerTOs) {
+            tableBuilder.rowValues(
+                    new LinkedList(Arrays.asList(loggerTO.getKey(), loggerTO.getLevel().getLevel().name())));
+        }
+        tableBuilder.build().print();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/47392366/client/cli/src/main/java/org/apache/syncope/client/cli/messages/Messages.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/messages/Messages.java b/client/cli/src/main/java/org/apache/syncope/client/cli/messages/Messages.java
index e2cb159..35550fd 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/messages/Messages.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/messages/Messages.java
@@ -22,6 +22,10 @@ public final class Messages {
 
     private static final String OPTION_COMMAND_MESSAGE_TEMPLATE = "\n - Usage: %s\n";
 
+    private static final String CREATED_MESSAGE_TEMPLATE = "%s %s successfully created";
+
+    private static final String UPDATED_MESSAGE_TEMPLATE = "%s %s successfully updated";
+
     private static final String DELETED_MESSAGE_TEMPLATE = "%s %s successfully deleted";
 
     private static final String DOESNT_EXIST_MESSAGE_TEMPLATE = "%s %s doesn't exist";
@@ -52,6 +56,14 @@ public final class Messages {
         printMessage(String.format(DOESNT_EXIST_MESSAGE_TEMPLATE, what, key));
     }
 
+    public static void printCreatedMessage(final String what, final String key) {
+        printMessage(String.format(CREATED_MESSAGE_TEMPLATE, what, key));
+    }
+
+    public static void printUpdatedMessage(final String what, final String key) {
+        printMessage(String.format(UPDATED_MESSAGE_TEMPLATE, what, key));
+    }
+
     public static void printDeletedMessage(final String what, final String key) {
         printMessage(String.format(DELETED_MESSAGE_TEMPLATE, what, key));
     }
@@ -59,7 +71,7 @@ public final class Messages {
     public static void printIdNotNumberDeletedMessage(final String what, final String key) {
         printMessage(String.format(ID_NOT_NUMBER_MESSAGE_TEMPLATE, key, what));
     }
-    
+
     public static void printNotBooleanDeletedMessage(final String what, final String key) {
         printMessage(String.format(NOT_BOOLEAN_MESSAGE_TEMPLATE, key, what));
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/47392366/client/cli/src/main/java/org/apache/syncope/client/cli/messages/Table.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/messages/Table.java b/client/cli/src/main/java/org/apache/syncope/client/cli/messages/Table.java
new file mode 100644
index 0000000..4f22980
--- /dev/null
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/messages/Table.java
@@ -0,0 +1,190 @@
+/*
+ * 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.cli.messages;
+
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.Set;
+import org.apache.commons.lang3.StringUtils;
+
+public final class Table {
+
+    private static final String TABLE_TITLE_FORMAT = "#  %s  #\n";
+
+    private final String title;
+
+    private final LinkedList<String> headers;
+
+    private final Set<LinkedList<String>> values;
+
+    private int columnsNumber;
+
+    private String[] tmpValuesArray;
+
+    private String tableContentFormat;
+
+    private int[] columnsSize;
+
+    private int tableWidth;
+
+    private String border = "";
+
+    private Table(
+            final String title,
+            final LinkedList<String> headers,
+            final Set<LinkedList<String>> values) {
+        this.title = title;
+        this.headers = headers;
+        this.values = values;
+    }
+
+    public void print() {
+        columnsNumber = headers.size();
+        tmpValuesArray = new String[columnsNumber];
+
+        buildTableContentFormat();
+        initializeColumnSize();
+        countTableWidth();
+
+        printBorder();
+        printTitle();
+        printBorder();
+        printHeaders();
+        printBorder();
+        printeContent();
+        printBorder();
+    }
+
+    private void buildTableContentFormat() {
+        final StringBuilder tableContentFormatBuilder = new StringBuilder("#");
+        for (int s = 0; s < columnsNumber; s++) {
+            tableContentFormatBuilder.append("  %s  #");
+        }
+        tableContentFormatBuilder.append("\n");
+        tableContentFormat = tableContentFormatBuilder.toString();
+    }
+
+    private void initializeColumnSize() {
+        columnsSize = new int[columnsNumber];
+        for (int j = 0; j < columnsSize.length; j++) {
+            columnsSize[j] = 0;
+        }
+
+        for (int i = 0; i < columnsSize.length; i++) {
+            if (headers.get(i).length() > columnsSize[i]) {
+                columnsSize[i] = headers.get(i).length();
+            }
+        }
+
+        for (final LinkedList<String> value : values) {
+            for (int j = 0; j < columnsSize.length; j++) {
+                if (value.get(j).length() > columnsSize[j]) {
+                    columnsSize[j] = value.get(j).length();
+                }
+            }
+        }
+    }
+
+    private void countTableWidth() {
+        int maxColumnValueSum = 0;
+        for (int j = 0; j < columnsSize.length; j++) {
+            maxColumnValueSum = maxColumnValueSum + columnsSize[j];
+        }
+
+        tableWidth = maxColumnValueSum + (columnsNumber * (2 + 2)) + columnsNumber + 1;
+    }
+
+    private void printBorder() {
+        if (border.isEmpty()) {
+            final StringBuilder borderBuilder = new StringBuilder();
+            for (int j = 0; j < tableWidth; j++) {
+                borderBuilder.append("#");
+            }
+            border = borderBuilder.toString();
+        }
+
+        System.out.println(border);
+    }
+
+    private void printTitle() {
+        System.out.format(TABLE_TITLE_FORMAT, StringUtils.center(" ", tableWidth - 6));
+        System.out.format(TABLE_TITLE_FORMAT, StringUtils.center(title.toUpperCase(), tableWidth - 6));
+        System.out.format(TABLE_TITLE_FORMAT, StringUtils.center(" ", tableWidth - 6));
+    }
+
+    public void printHeaders() {
+        printColumnSpace();
+
+        for (int h = 0; h < columnsNumber; h++) {
+            tmpValuesArray[h] = StringUtils.center(headers.get(h).toUpperCase(), columnsSize[h]);
+        }
+
+        System.out.format(tableContentFormat, tmpValuesArray);
+
+        printColumnSpace();
+    }
+
+    private void printeContent() {
+        printColumnSpace();
+
+        for (LinkedList<String> value : values) {
+            for (int j = 0; j < columnsNumber; j++) {
+                tmpValuesArray[j] = StringUtils.center(value.get(j), columnsSize[j]);
+            }
+            System.out.format(tableContentFormat, tmpValuesArray);
+        }
+
+        printColumnSpace();
+    }
+
+    private void printColumnSpace() {
+        for (int h = 0; h < columnsNumber; h++) {
+            tmpValuesArray[h] = StringUtils.center(" ", columnsSize[h]);
+        }
+
+        System.out.format(tableContentFormat, tmpValuesArray);
+    }
+    
+    public static class TableBuilder {
+
+        private final LinkedList<String> headers = new LinkedList<>();
+
+        private final Set<LinkedList<String>> values = new LinkedHashSet<>();
+
+        private final String title;
+
+        public TableBuilder(final String title) {
+            this.title = title;
+        }
+
+        public TableBuilder header(final String header) {
+            headers.add(header);
+            return this;
+        }
+
+        public TableBuilder rowValues(final LinkedList<String> row) {
+            values.add(row);
+            return this;
+        }
+
+        public Table build() {
+            return new Table(title, headers, values);
+        }
+    }
+}