You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@batchee.apache.org by rm...@apache.org on 2015/12/02 16:04:05 UTC

incubator-batchee git commit: BATCHEE-81 getting rid of airline

Repository: incubator-batchee
Updated Branches:
  refs/heads/master 711054266 -> a5191447f


BATCHEE-81 getting rid of airline


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

Branch: refs/heads/master
Commit: a5191447fe8958949e31b4d948c9995a7e1bc132
Parents: 7110542
Author: Romain Manni-Bucau <rm...@gmail.com>
Authored: Wed Dec 2 16:03:52 2015 +0100
Committer: Romain Manni-Bucau <rm...@gmail.com>
Committed: Wed Dec 2 16:03:58 2015 +0100

----------------------------------------------------------------------
 tools/cli/pom.xml                               |  16 +-
 .../java/org/apache/batchee/cli/BatchEECLI.java | 198 +++++++++++++++++--
 .../org/apache/batchee/cli/command/Abandon.java |   6 +-
 .../batchee/cli/command/CliConfiguration.java   |   3 +
 .../apache/batchee/cli/command/Eviction.java    |   6 +-
 .../apache/batchee/cli/command/Executions.java  |   6 +-
 .../org/apache/batchee/cli/command/Exit.java    |  32 ---
 .../apache/batchee/cli/command/Instances.java   |  10 +-
 .../batchee/cli/command/JobOperatorCommand.java |  42 ++--
 .../org/apache/batchee/cli/command/Names.java   |   2 +-
 .../org/apache/batchee/cli/command/Restart.java |   6 +-
 .../org/apache/batchee/cli/command/Running.java |   2 +-
 .../batchee/cli/command/SocketCommand.java      |   4 +-
 .../cli/command/SocketConfigurableCommand.java  |   6 +-
 .../org/apache/batchee/cli/command/Start.java   |   6 +-
 .../batchee/cli/command/StartableCommand.java   |   8 +-
 .../org/apache/batchee/cli/command/Status.java  |   2 +-
 .../org/apache/batchee/cli/command/Stop.java    |   6 +-
 .../apache/batchee/cli/command/UserCommand.java |  22 ---
 .../batchee/cli/command/api/Arguments.java      |  29 +++
 .../apache/batchee/cli/command/api/Command.java |  30 +++
 .../apache/batchee/cli/command/api/Exit.java    |  32 +++
 .../apache/batchee/cli/command/api/Option.java  |  31 +++
 .../batchee/cli/command/api/UserCommand.java    |  23 +++
 .../java/org/apache/batchee/cli/MainTest.java   |  31 +++
 .../org/apache/batchee/cli/command/User1.java   |   3 +-
 .../org/apache/batchee/cli/command/User2.java   |   3 +-
 27 files changed, 423 insertions(+), 142 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/pom.xml
----------------------------------------------------------------------
diff --git a/tools/cli/pom.xml b/tools/cli/pom.xml
index 7cd6b4d..b4b0854 100644
--- a/tools/cli/pom.xml
+++ b/tools/cli/pom.xml
@@ -49,22 +49,16 @@
       <version>${project.version}</version>
     </dependency>
     <dependency>
-      <groupId>io.airlift</groupId>
-      <artifactId>airline</artifactId>
-      <version>0.7</version>
-      <exclusions>
-        <exclusion>
-          <groupId>javax.inject</groupId>
-          <artifactId>javax.inject</artifactId>
-        </exclusion>
-      </exclusions>
-    </dependency>
-    <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-lang3</artifactId>
       <version>3.1</version>
     </dependency>
     <dependency>
+      <groupId>commons-cli</groupId>
+      <artifactId>commons-cli</artifactId>
+      <version>1.3.1</version>
+    </dependency>
+    <dependency>
       <groupId>commons-io</groupId>
       <artifactId>commons-io</artifactId>
       <version>2.4</version>

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/main/java/org/apache/batchee/cli/BatchEECLI.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/BatchEECLI.java b/tools/cli/src/main/java/org/apache/batchee/cli/BatchEECLI.java
index a10775e..eec4d69 100644
--- a/tools/cli/src/main/java/org/apache/batchee/cli/BatchEECLI.java
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/BatchEECLI.java
@@ -16,14 +16,11 @@
  */
 package org.apache.batchee.cli;
 
-import io.airlift.airline.Cli;
-import io.airlift.airline.Help;
-import io.airlift.airline.ParseException;
 import org.apache.batchee.cli.command.Abandon;
 import org.apache.batchee.cli.command.CliConfiguration;
 import org.apache.batchee.cli.command.Eviction;
 import org.apache.batchee.cli.command.Executions;
-import org.apache.batchee.cli.command.Exit;
+import org.apache.batchee.cli.command.api.Exit;
 import org.apache.batchee.cli.command.Instances;
 import org.apache.batchee.cli.command.Names;
 import org.apache.batchee.cli.command.Restart;
@@ -31,19 +28,36 @@ import org.apache.batchee.cli.command.Running;
 import org.apache.batchee.cli.command.Start;
 import org.apache.batchee.cli.command.Status;
 import org.apache.batchee.cli.command.Stop;
-import org.apache.batchee.cli.command.UserCommand;
+import org.apache.batchee.cli.command.api.Command;
+import org.apache.batchee.cli.command.api.UserCommand;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
 
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
 import java.util.ServiceLoader;
+import java.util.TreeMap;
 
 import static java.lang.ClassLoader.getSystemClassLoader;
+import static java.util.Arrays.asList;
 
 public class BatchEECLI {
     public static void main(final String[] args) {
@@ -105,31 +119,115 @@ public class BatchEECLI {
             }
         };
 
-        final Cli.CliBuilder<Runnable> builder = Cli.<Runnable>builder(cliConfiguration.name())
-                .withDescription(cliConfiguration.description())
-                .withDefaultCommand(Help.class);
+        final Map<String, Class<? extends Runnable>> commands = new TreeMap<String, Class<? extends Runnable>>();
         if (cliConfiguration.addDefaultCommands()) {
-            builder.withCommands(Help.class,
-                Names.class,
-                Start.class, Restart.class,
-                Status.class, Running.class,
-                Stop.class, Abandon.class,
-                Instances.class, Executions.class,
-                Eviction.class);
+            for (final Class<? extends Runnable> type :
+                Arrays.asList(Names.class,
+                    Start.class, Restart.class,
+                    Status.class, Running.class,
+                    Stop.class, Abandon.class,
+                    Instances.class, Executions.class,
+                    Eviction.class)) {
+                addCommand(commands, type);
+            }
         }
         final Iterator<Class<? extends UserCommand>> userCommands = cliConfiguration.userCommands();
         if (userCommands != null) {
             while (userCommands.hasNext()) {
-                builder.withCommand(userCommands.next());
+                addCommand(commands, userCommands.next());
+            }
+        }
+
+        if (args == null || args.length == 0) {
+            System.err.print(help(commands));
+            return;
+        }
+
+        final Class<? extends Runnable> cmd = commands.get(args[0]);
+        if (cmd == null) {
+            if (args[0].equals("help")) {
+                if (args.length > 1) {
+                    final Class<? extends Runnable> helpCmd = commands.get(args[1]);
+                    if (helpCmd != null) {
+                        printHelp(helpCmd.getAnnotation(Command.class), buildOptions(helpCmd, new HashMap<String, Field>()));
+                        return;
+                    }
+                } // else let use the default help
             }
+            System.err.print(help(commands));
+            return;
         }
 
-        final Cli<Runnable> parser = builder.build();
+        // build the command now
+        final Command command = cmd.getAnnotation(Command.class);
+        if (command == null) {
+            System.err.print(help(commands));
+            return;
+        }
+
+        final Map<String, Field> fields = new HashMap<String, Field>();
+        final Options options = buildOptions(cmd, fields);
+
+        final Collection<String> newArgs = new ArrayList<String>(asList(args));
+        newArgs.remove(newArgs.iterator().next());
 
+        final CommandLineParser parser = new DefaultParser();
         try {
-            cliConfiguration.decorate(parser.parse(args)).run();
+            final CommandLine line = parser.parse(options, newArgs.toArray(new String[newArgs.size()]));
+
+            final Runnable commandInstance = cmd.newInstance();
+            if (!newArgs.isEmpty()) { // we have few commands we can execute without args even if we have a bunch of config
+                for (final Map.Entry<String, Field> option : fields.entrySet()) {
+                    final String key = option.getKey();
+                    if (key.isEmpty()) { // arguments, not an option
+                        final List<String> list = line.getArgList();
+                        if (list != null) {
+                            final Field field = option.getValue();
+                            final Type expectedType = field.getGenericType();
+                            if (ParameterizedType.class.isInstance(expectedType)) {
+                                final ParameterizedType pt = ParameterizedType.class.cast(expectedType);
+                                if ((pt.getRawType() == List.class || pt.getRawType() == Collection.class)
+                                    && pt.getActualTypeArguments().length == 1 && pt.getActualTypeArguments()[0] == String.class) {
+                                    field.set(commandInstance, list);
+                                } else {
+                                    throw new IllegalArgumentException("@Arguments only supports List<String>");
+                                }
+                            } else {
+                                throw new IllegalArgumentException("@Arguments only supports List<String>");
+                            }
+                        }
+                    } else {
+                        final String value = line.getOptionValue(key);
+                        if (value != null) {
+                            final Field field = option.getValue();
+                            final Class<?> expectedType = field.getType();
+                            if (String.class == expectedType) {
+                                field.set(commandInstance, value);
+                            } else if (long.class == expectedType) {
+                                field.set(commandInstance, Long.parseLong(value));
+                            } else if (int.class == expectedType) {
+                                field.set(commandInstance, Integer.parseInt(value));
+                            } else if (boolean.class == expectedType) {
+                                field.set(commandInstance, Boolean.parseBoolean(value));
+                            } else if (short.class == expectedType) {
+                                field.set(commandInstance, Short.parseShort(value));
+                            } else if (byte.class == expectedType) {
+                                field.set(commandInstance, Byte.parseByte(value));
+                            } else {
+                                try {
+                                    field.set(commandInstance, expectedType.getMethod("fromString", String.class)
+                                        .invoke(null, value));
+                                } catch (final Exception e) {
+                                    throw new IllegalArgumentException(expectedType + " not supported as option with value '" + value + "'");
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            cliConfiguration.decorate(commandInstance).run();
         } catch (final ParseException e) {
-            parser.parse("help").run();
+            printHelp(command, options);
         } catch (final RuntimeException e) {
             Class<?> current = e.getClass();
             while (current != null) {
@@ -140,9 +238,71 @@ public class BatchEECLI {
                 current = current.getSuperclass();
             }
             throw e;
+        } catch (final InstantiationException e) {
+            throw new IllegalStateException(e);
+        } catch (final IllegalAccessException e) {
+            throw new IllegalStateException(e);
         }
     }
 
+    private static void printHelp(final Command command, final Options options) {
+        new HelpFormatter().printHelp(command.name(), command.description(), options, null, true);
+    }
+
+    private static Options buildOptions(final Class<? extends Runnable> cmd, Map<String, Field> fields) {
+        final Options options = new Options();
+        Class<?> it = cmd;
+        while (it != null) {
+            for (final Field f : it.getDeclaredFields()) {
+                final org.apache.batchee.cli.command.api.Option option = f.getAnnotation(org.apache.batchee.cli.command.api.Option.class);
+                final org.apache.batchee.cli.command.api.Arguments arguments = f.getAnnotation(org.apache.batchee.cli.command.api.Arguments.class);
+                if (option != null && arguments != null) {
+                    throw new IllegalArgumentException("An @Option can't get @Arguments: " + f);
+                }
+
+                if (option != null) {
+                    final String name = option.name();
+                    options.addOption(Option.builder(name).desc(option.description()).hasArg().build());
+                    fields.put(name, f);
+                    f.setAccessible(true);
+                } else if (arguments != null) {
+                    if (fields.put("", f) != null) {
+                        throw new IllegalArgumentException("A command can only have a single @Arguments");
+                    }
+                    f.setAccessible(true);
+                }
+            }
+            it = it.getSuperclass();
+        }
+        return options;
+    }
+
+    private static String help(final Map<String, Class<? extends Runnable>> commands) {
+        final StringBuilder builder = new StringBuilder();
+        for (final Map.Entry<String, Class<? extends Runnable>> cmd : commands.entrySet()) {
+            final Command c = cmd.getValue().getAnnotation(Command.class);
+            if (c == null) {
+                throw new IllegalArgumentException("Missing @Command on " + cmd.getValue());
+            }
+            builder.append(c.name());
+            if (!c.description().isEmpty()) {
+                builder.append(": ").append(c.description());
+            }
+            builder.append(System.getProperty("line.separator"));
+        }
+        return builder.toString();
+    }
+
+    private static void addCommand(final Map<String, Class<? extends Runnable>> commands, final Class<? extends Runnable> type) {
+        final Command command = type.getAnnotation(Command.class);
+        if (command == null) {
+            throw new IllegalArgumentException(type + " is not a command, missing @Command");
+        }
+
+        final String name = command.name();
+        commands.put(name, type);
+    }
+
     private BatchEECLI() {
         // no-op
     }

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/main/java/org/apache/batchee/cli/command/Abandon.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/command/Abandon.java b/tools/cli/src/main/java/org/apache/batchee/cli/command/Abandon.java
index eed60af..688b76d 100644
--- a/tools/cli/src/main/java/org/apache/batchee/cli/command/Abandon.java
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/command/Abandon.java
@@ -16,12 +16,12 @@
  */
 package org.apache.batchee.cli.command;
 
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
+import org.apache.batchee.cli.command.api.Command;
+import org.apache.batchee.cli.command.api.Option;
 
 @Command(name = "abandon", description = "abandon a batch from its id")
 public class Abandon extends SocketCommand {
-    @Option(name = "-id", description = "id of the batch to abandon", required = true)
+    @Option(name = "id", description = "id of the batch to abandon", required = true)
     private long id;
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/main/java/org/apache/batchee/cli/command/CliConfiguration.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/command/CliConfiguration.java b/tools/cli/src/main/java/org/apache/batchee/cli/command/CliConfiguration.java
index 9028d3d..b8a8522 100644
--- a/tools/cli/src/main/java/org/apache/batchee/cli/command/CliConfiguration.java
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/command/CliConfiguration.java
@@ -16,6 +16,8 @@
  */
 package org.apache.batchee.cli.command;
 
+import org.apache.batchee.cli.command.api.UserCommand;
+
 import java.util.Iterator;
 
 public interface CliConfiguration {
@@ -24,4 +26,5 @@ public interface CliConfiguration {
     boolean addDefaultCommands();
     Iterator<Class<? extends UserCommand>> userCommands();
     Runnable decorate(Runnable task);
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/main/java/org/apache/batchee/cli/command/Eviction.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/command/Eviction.java b/tools/cli/src/main/java/org/apache/batchee/cli/command/Eviction.java
index f034612..fcb52a3 100644
--- a/tools/cli/src/main/java/org/apache/batchee/cli/command/Eviction.java
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/command/Eviction.java
@@ -16,8 +16,8 @@
  */
 package org.apache.batchee.cli.command;
 
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
+import org.apache.batchee.cli.command.api.Command;
+import org.apache.batchee.cli.command.api.Option;
 import org.apache.batchee.container.services.ServicesManager;
 import org.apache.batchee.spi.PersistenceManagerService;
 
@@ -27,7 +27,7 @@ import java.util.Date;
 
 @Command(name = "evict", description = "remove old data, uses embedded configuration (no JAXRS support yet)")
 public class Eviction implements Runnable {
-    @Option(name = "-until", description = "date until when the eviction will occur (excluded), YYYYMMDD format", required = true)
+    @Option(name = "until", description = "date until when the eviction will occur (excluded), YYYYMMDD format", required = true)
     private String date;
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/main/java/org/apache/batchee/cli/command/Executions.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/command/Executions.java b/tools/cli/src/main/java/org/apache/batchee/cli/command/Executions.java
index 40d4253..dcb4f44 100644
--- a/tools/cli/src/main/java/org/apache/batchee/cli/command/Executions.java
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/command/Executions.java
@@ -16,8 +16,8 @@
  */
 package org.apache.batchee.cli.command;
 
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
+import org.apache.batchee.cli.command.api.Command;
+import org.apache.batchee.cli.command.api.Option;
 import org.apache.batchee.container.impl.JobInstanceImpl;
 import org.apache.commons.lang3.StringUtils;
 
@@ -26,7 +26,7 @@ import java.util.List;
 
 @Command(name = "executions", description = "list executions")
 public class Executions extends JobOperatorCommand {
-    @Option(name = "-id", description = "instance id", required = true)
+    @Option(name = "id", description = "instance id", required = true)
     private long id;
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/main/java/org/apache/batchee/cli/command/Exit.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/command/Exit.java b/tools/cli/src/main/java/org/apache/batchee/cli/command/Exit.java
deleted file mode 100644
index f24f3ac..0000000
--- a/tools/cli/src/main/java/org/apache/batchee/cli/command/Exit.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.batchee.cli.command;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.TYPE;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-@Retention(RUNTIME)
-@Target(TYPE)
-public @interface Exit {
-    /**
-     * @return the exit code to use.
-     */
-    int value();
-}

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/main/java/org/apache/batchee/cli/command/Instances.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/command/Instances.java b/tools/cli/src/main/java/org/apache/batchee/cli/command/Instances.java
index 62f7682..8d2a2f5 100644
--- a/tools/cli/src/main/java/org/apache/batchee/cli/command/Instances.java
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/command/Instances.java
@@ -16,8 +16,8 @@
  */
 package org.apache.batchee.cli.command;
 
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
+import org.apache.batchee.cli.command.api.Command;
+import org.apache.batchee.cli.command.api.Option;
 
 import javax.batch.operations.JobOperator;
 import javax.batch.runtime.JobInstance;
@@ -25,13 +25,13 @@ import java.util.List;
 
 @Command(name = "instances", description = "list instances")
 public class Instances extends JobOperatorCommand {
-    @Option(name = "-name", description = "name of the batch to start", required = true)
+    @Option(name = "name", description = "name of the batch to start", required = true)
     private String name;
 
-    @Option(name = "-start", description = "start of the list of job instance to query")
+    @Option(name = "start", description = "start of the list of job instance to query")
     private int start = 0;
 
-    @Option(name = "-count", description = "number of instance to query")
+    @Option(name = "count", description = "number of instance to query")
     private int count = 100;
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/main/java/org/apache/batchee/cli/command/JobOperatorCommand.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/command/JobOperatorCommand.java b/tools/cli/src/main/java/org/apache/batchee/cli/command/JobOperatorCommand.java
index c8c3c23..7357e18 100644
--- a/tools/cli/src/main/java/org/apache/batchee/cli/command/JobOperatorCommand.java
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/command/JobOperatorCommand.java
@@ -16,8 +16,8 @@
  */
 package org.apache.batchee.cli.command;
 
-import io.airlift.airline.Option;
 import org.apache.batchee.cli.classloader.ChildFirstURLClassLoader;
+import org.apache.batchee.cli.command.api.Option;
 import org.apache.batchee.cli.lifecycle.Lifecycle;
 import org.apache.batchee.cli.zip.Zips;
 import org.apache.batchee.container.exception.BatchContainerRuntimeException;
@@ -51,66 +51,66 @@ import static java.lang.Thread.currentThread;
 public abstract class JobOperatorCommand implements Runnable {
     // Remote config
 
-    @Option(name = "-url", description = "when using JAXRS the batchee resource url")
+    @Option(name = "url", description = "when using JAXRS the batchee resource url")
     protected String baseUrl = null;
 
-    @Option(name = "-json", description = "when using JAXRS the json provider")
+    @Option(name = "json", description = "when using JAXRS the json provider")
     private String jsonProvider = null;
 
-    @Option(name = "-user", description = "when using JAXRS the username")
+    @Option(name = "user", description = "when using JAXRS the username")
     private String username = null;
 
-    @Option(name = "-password", description = "when using JAXRS the password")
+    @Option(name = "password", description = "when using JAXRS the password")
     private String password = null;
 
-    @Option(name = "-auth", description = "when using JAXRS the authentication type (Basic)")
+    @Option(name = "auth", description = "when using JAXRS the authentication type (Basic)")
     private String type = "Basic";
 
-    @Option(name = "-hostnameVerifier", description = "when using JAXRS the hostname verifier")
+    @Option(name = "hostnameVerifier", description = "when using JAXRS the hostname verifier")
     private String hostnameVerifier = null;
 
-    @Option(name = "-keystorePassword", description = "when using JAXRS the keystorePassword")
+    @Option(name = "keystorePassword", description = "when using JAXRS the keystorePassword")
     private String keystorePassword = null;
 
-    @Option(name = "-keystoreType", description = "when using JAXRS the keystoreType (JKS)")
+    @Option(name = "keystoreType", description = "when using JAXRS the keystoreType (JKS)")
     private String keystoreType = "JKS";
 
-    @Option(name = "-keystorePath", description = "when using JAXRS the keystorePath")
+    @Option(name = "keystorePath", description = "when using JAXRS the keystorePath")
     private String keystorePath = null;
 
-    @Option(name = "-sslContextType", description = "when using JAXRS the sslContextType (TLS)")
+    @Option(name = "sslContextType", description = "when using JAXRS the sslContextType (TLS)")
     private String sslContextType = "TLS";
 
-    @Option(name = "-keyManagerType", description = "when using JAXRS the keyManagerType (SunX509)")
+    @Option(name = "keyManagerType", description = "when using JAXRS the keyManagerType (SunX509)")
     private String keyManagerType = "SunX509";
 
-    @Option(name = "-keyManagerPath", description = "when using JAXRS the keyManagerPath")
+    @Option(name = "keyManagerPath", description = "when using JAXRS the keyManagerPath")
     private String keyManagerPath = null;
 
-    @Option(name = "-trustManagerAlgorithm", description = "when using JAXRS the trustManagerAlgorithm")
+    @Option(name = "trustManagerAlgorithm", description = "when using JAXRS the trustManagerAlgorithm")
     private String trustManagerAlgorithm = null;
 
-    @Option(name = "-trustManagerProvider", description = "when using JAXRS the trustManagerProvider")
+    @Option(name = "trustManagerProvider", description = "when using JAXRS the trustManagerProvider")
     private String trustManagerProvider = null;
 
     // local config
 
-    @Option(name = "-lifecycle", description = "the lifecycle class to use")
+    @Option(name = "lifecycle", description = "the lifecycle class to use")
     private String lifecycle = null;
 
-    @Option(name = "-libs", description = "folder containing additional libraries, the folder is added too to the loader")
+    @Option(name = "libs", description = "folder containing additional libraries, the folder is added too to the loader")
     private String libs = null;
 
-    @Option(name = "-archive", description = "a bar archive")
+    @Option(name = "archive", description = "a bar archive")
     private String archive = null;
 
-    @Option(name = "-work", description = "work directory (default to java.io.tmp/work)")
+    @Option(name = "work", description = "work directory (default to java.io.tmp/work)")
     private String work = System.getProperty("batchee.home", System.getProperty("java.io.tmpdir")) + "/work";
 
-    @Option(name = "-shared-libs", description = "folder containing shared libraries, the folder is added too to the loader")
+    @Option(name = "sharedLibs", description = "folder containing shared libraries, the folder is added too to the loader")
     private String sharedLibs = null;
 
-    @Option(name = "-add-folder-to-loader", description = "force shared lib and libs folders to be added to the classloader")
+    @Option(name = "addFolderToLoader", description = "force shared lib and libs folders to be added to the classloader")
     private boolean addFolderToLoader = false;
 
     protected JobOperator operator() {

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/main/java/org/apache/batchee/cli/command/Names.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/command/Names.java b/tools/cli/src/main/java/org/apache/batchee/cli/command/Names.java
index 6ef2282..a62aa32 100644
--- a/tools/cli/src/main/java/org/apache/batchee/cli/command/Names.java
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/command/Names.java
@@ -16,7 +16,7 @@
  */
 package org.apache.batchee.cli.command;
 
-import io.airlift.airline.Command;
+import org.apache.batchee.cli.command.api.Command;
 
 import java.io.File;
 import java.io.FilenameFilter;

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/main/java/org/apache/batchee/cli/command/Restart.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/command/Restart.java b/tools/cli/src/main/java/org/apache/batchee/cli/command/Restart.java
index be345b6..c4ad8f2 100644
--- a/tools/cli/src/main/java/org/apache/batchee/cli/command/Restart.java
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/command/Restart.java
@@ -16,14 +16,14 @@
  */
 package org.apache.batchee.cli.command;
 
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
+import org.apache.batchee.cli.command.api.Command;
+import org.apache.batchee.cli.command.api.Option;
 
 import javax.batch.operations.JobOperator;
 
 @Command(name = "restart", description = "restart a batch")
 public class Restart extends StartableCommand {
-    @Option(name = "-id", description = "id of the failed batch", required = true)
+    @Option(name = "id", description = "id of the failed batch", required = true)
     private long id;
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/main/java/org/apache/batchee/cli/command/Running.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/command/Running.java b/tools/cli/src/main/java/org/apache/batchee/cli/command/Running.java
index 7e0ff0a..5418131 100644
--- a/tools/cli/src/main/java/org/apache/batchee/cli/command/Running.java
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/command/Running.java
@@ -16,7 +16,7 @@
  */
 package org.apache.batchee.cli.command;
 
-import io.airlift.airline.Command;
+import org.apache.batchee.cli.command.api.Command;
 
 import javax.batch.operations.JobOperator;
 import javax.batch.operations.NoSuchJobException;

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/main/java/org/apache/batchee/cli/command/SocketCommand.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/command/SocketCommand.java b/tools/cli/src/main/java/org/apache/batchee/cli/command/SocketCommand.java
index 3785d14..947c664 100644
--- a/tools/cli/src/main/java/org/apache/batchee/cli/command/SocketCommand.java
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/command/SocketCommand.java
@@ -16,7 +16,7 @@
  */
 package org.apache.batchee.cli.command;
 
-import io.airlift.airline.Option;
+import org.apache.batchee.cli.command.api.Option;
 import org.apache.batchee.container.exception.BatchContainerRuntimeException;
 import org.apache.commons.io.IOUtils;
 
@@ -28,7 +28,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
 public abstract class SocketCommand extends SocketConfigurableCommand {
-    @Option(name = "-timeout", description = "timeout for socket case")
+    @Option(name = "timeout", description = "timeout for socket case")
     private int timeout = 60000;
 
     protected boolean shouldUseSocket() {

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/main/java/org/apache/batchee/cli/command/SocketConfigurableCommand.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/command/SocketConfigurableCommand.java b/tools/cli/src/main/java/org/apache/batchee/cli/command/SocketConfigurableCommand.java
index 8241745..7a15f86 100644
--- a/tools/cli/src/main/java/org/apache/batchee/cli/command/SocketConfigurableCommand.java
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/command/SocketConfigurableCommand.java
@@ -16,12 +16,12 @@
  */
 package org.apache.batchee.cli.command;
 
-import io.airlift.airline.Option;
+import org.apache.batchee.cli.command.api.Option;
 
 public abstract class SocketConfigurableCommand extends JobOperatorCommand {
-    @Option(name = "-wait", description = "should wait the end of the batch", arity = 1)
+    @Option(name = "wait", description = "should wait the end of the batch")
     protected boolean wait = true;
 
-    @Option(name = "-socket", description = "socket listening for stop/abandon commands")
+    @Option(name = "socket", description = "socket listening for stop/abandon commands")
     protected int adminSocket = -1;
 }

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/main/java/org/apache/batchee/cli/command/Start.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/command/Start.java b/tools/cli/src/main/java/org/apache/batchee/cli/command/Start.java
index 4b310c6..119c2d8 100644
--- a/tools/cli/src/main/java/org/apache/batchee/cli/command/Start.java
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/command/Start.java
@@ -16,14 +16,14 @@
  */
 package org.apache.batchee.cli.command;
 
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
+import org.apache.batchee.cli.command.api.Command;
+import org.apache.batchee.cli.command.api.Option;
 
 import javax.batch.operations.JobOperator;
 
 @Command(name = "start", description = "start a batch")
 public class Start extends StartableCommand {
-    @Option(name = "-name", description = "name of the batch to start", required = true)
+    @Option(name = "name", description = "name of the batch to start", required = true)
     private String name;
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/main/java/org/apache/batchee/cli/command/StartableCommand.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/command/StartableCommand.java b/tools/cli/src/main/java/org/apache/batchee/cli/command/StartableCommand.java
index 6e16962..a3484ae 100644
--- a/tools/cli/src/main/java/org/apache/batchee/cli/command/StartableCommand.java
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/command/StartableCommand.java
@@ -16,8 +16,8 @@
  */
 package org.apache.batchee.cli.command;
 
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Option;
+import org.apache.batchee.cli.command.api.Arguments;
+import org.apache.batchee.cli.command.api.Option;
 import org.apache.batchee.util.Batches;
 import org.apache.commons.io.IOUtils;
 
@@ -41,10 +41,10 @@ public abstract class StartableCommand extends SocketConfigurableCommand {
     protected List<String> properties;
 
     // some unix systems dont support negative systems.
-    @Option(name = "-error-exit-code", description = "exit code if any error occurs, should be > 0 or ignored")
+    @Option(name = "errorExitCode", description = "exit code if any error occurs, should be > 0 or ignored")
     protected int errorExitCode = -1;
 
-    @Option(name = "-failure-exit-code", description = "exit code if the batch result is not completed, should be > 0 and wait should be true or ignored")
+    @Option(name = "failureExitCode", description = "exit code if the batch result is not completed, should be > 0 and wait should be true or ignored")
     protected int failureExitCode = -1;
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/main/java/org/apache/batchee/cli/command/Status.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/command/Status.java b/tools/cli/src/main/java/org/apache/batchee/cli/command/Status.java
index b5eb0a6..e7ee23a 100644
--- a/tools/cli/src/main/java/org/apache/batchee/cli/command/Status.java
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/command/Status.java
@@ -16,7 +16,7 @@
  */
 package org.apache.batchee.cli.command;
 
-import io.airlift.airline.Command;
+import org.apache.batchee.cli.command.api.Command;
 import org.apache.commons.lang3.StringUtils;
 
 import javax.batch.operations.JobOperator;

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/main/java/org/apache/batchee/cli/command/Stop.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/command/Stop.java b/tools/cli/src/main/java/org/apache/batchee/cli/command/Stop.java
index d183027..fd86480 100644
--- a/tools/cli/src/main/java/org/apache/batchee/cli/command/Stop.java
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/command/Stop.java
@@ -16,12 +16,12 @@
  */
 package org.apache.batchee.cli.command;
 
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
+import org.apache.batchee.cli.command.api.Command;
+import org.apache.batchee.cli.command.api.Option;
 
 @Command(name = "stop", description = "stop a batch from its id")
 public class Stop extends SocketCommand {
-    @Option(name = "-id", description = "id of the batch to stop", required = true)
+    @Option(name = "id", description = "id of the batch to stop", required = true)
     private long id;
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/main/java/org/apache/batchee/cli/command/UserCommand.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/command/UserCommand.java b/tools/cli/src/main/java/org/apache/batchee/cli/command/UserCommand.java
deleted file mode 100644
index 3309e76..0000000
--- a/tools/cli/src/main/java/org/apache/batchee/cli/command/UserCommand.java
+++ /dev/null
@@ -1,22 +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.batchee.cli.command;
-
-// use io.airlift.airline.Command and more generally io.airlift.airline.*
-// and register your command using META-INF/services/org.apache.batchee.cli.command.UserCommand mecanism
-public interface UserCommand extends Runnable {
-}

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/main/java/org/apache/batchee/cli/command/api/Arguments.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/command/api/Arguments.java b/tools/cli/src/main/java/org/apache/batchee/cli/command/api/Arguments.java
new file mode 100644
index 0000000..dfb0cf2
--- /dev/null
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/command/api/Arguments.java
@@ -0,0 +1,29 @@
+/*
+ * 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.batchee.cli.command.api;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Target(FIELD)
+@Retention(RUNTIME)
+public @interface Arguments {
+    String description();
+}

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/main/java/org/apache/batchee/cli/command/api/Command.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/command/api/Command.java b/tools/cli/src/main/java/org/apache/batchee/cli/command/api/Command.java
new file mode 100644
index 0000000..fcf65ff
--- /dev/null
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/command/api/Command.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.batchee.cli.command.api;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Target(TYPE)
+@Retention(RUNTIME)
+public @interface Command {
+    String name();
+    String description() default "";
+}

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/main/java/org/apache/batchee/cli/command/api/Exit.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/command/api/Exit.java b/tools/cli/src/main/java/org/apache/batchee/cli/command/api/Exit.java
new file mode 100644
index 0000000..0e0c1e6
--- /dev/null
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/command/api/Exit.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.batchee.cli.command.api;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Retention(RUNTIME)
+@Target(TYPE)
+public @interface Exit {
+    /**
+     * @return the exit code to use.
+     */
+    int value();
+}

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/main/java/org/apache/batchee/cli/command/api/Option.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/command/api/Option.java b/tools/cli/src/main/java/org/apache/batchee/cli/command/api/Option.java
new file mode 100644
index 0000000..5970188
--- /dev/null
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/command/api/Option.java
@@ -0,0 +1,31 @@
+/*
+ * 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.batchee.cli.command.api;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Target(FIELD)
+@Retention(RUNTIME)
+public @interface Option {
+    String name();
+    String description();
+    boolean required() default false;
+}

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/main/java/org/apache/batchee/cli/command/api/UserCommand.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/command/api/UserCommand.java b/tools/cli/src/main/java/org/apache/batchee/cli/command/api/UserCommand.java
new file mode 100644
index 0000000..36fb084
--- /dev/null
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/command/api/UserCommand.java
@@ -0,0 +1,23 @@
+/*
+ * 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.batchee.cli.command.api;
+
+// use org.apache.batchee.cli.command.api.*
+// and register your command using META-INF/services/org.apache.batchee.cli.command.UserCommand mecanism
+// or a custom org.apache.batchee.cli.command.CliConfiguration
+public interface UserCommand extends Runnable {
+}

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/test/java/org/apache/batchee/cli/MainTest.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/test/java/org/apache/batchee/cli/MainTest.java b/tools/cli/src/test/java/org/apache/batchee/cli/MainTest.java
index 2774fcc..c3680ef 100644
--- a/tools/cli/src/test/java/org/apache/batchee/cli/MainTest.java
+++ b/tools/cli/src/test/java/org/apache/batchee/cli/MainTest.java
@@ -21,6 +21,7 @@ import org.apache.batchee.util.Batches;
 import org.junit.FixMethodOrder;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.contrib.java.lang.system.StandardErrorStreamLog;
 import org.junit.contrib.java.lang.system.StandardOutputStreamLog;
 import org.junit.runners.MethodSorters;
 
@@ -37,6 +38,36 @@ import static org.junit.Assert.assertThat;
 public class MainTest {
     @Rule
     public final StandardOutputStreamLog stdout = new StandardOutputStreamLog();
+    @Rule
+    public final StandardErrorStreamLog stderr = new StandardErrorStreamLog();
+
+    @Test
+    public void argHelp() {
+        main(null);
+        assertEquals(
+            "abandon: abandon a batch from its id\n" +
+            "evict: remove old data, uses embedded configuration (no JAXRS support yet)\n" +
+            "executions: list executions\n" +
+            "instances: list instances\n" +
+            "names: list known batches\n" +
+            "restart: restart a batch\n" +
+            "running: list running batches\n" +
+            "start: start a batch\n" +
+            "status: list last batches statuses\n" +
+            "stop: stop a batch from its id\n" +
+            "user1\n" +
+            "user2\n", stderr.getLog().replace(System.getProperty("line.separator"), "\n"));
+    }
+
+    @Test
+    public void helpCommand() {
+        main(new String[] { "help", "evict" }); // using a simple command to avoid a big block for nothing
+        assertEquals(
+            "usage: evict [-until <arg>]\n" +
+            "remove old data, uses embedded configuration (no JAXRS support yet)\n" +
+            " -until <arg>   date until when the eviction will occur (excluded),\n" +
+            "                YYYYMMDD format\n", stdout.getLog().replace(System.getProperty("line.separator"), "\n"));
+    }
 
     @Test
     public void start() {

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/test/java/org/apache/batchee/cli/command/User1.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/test/java/org/apache/batchee/cli/command/User1.java b/tools/cli/src/test/java/org/apache/batchee/cli/command/User1.java
index 1688693..64641b3 100644
--- a/tools/cli/src/test/java/org/apache/batchee/cli/command/User1.java
+++ b/tools/cli/src/test/java/org/apache/batchee/cli/command/User1.java
@@ -16,7 +16,8 @@
  */
 package org.apache.batchee.cli.command;
 
-import io.airlift.airline.Command;
+import org.apache.batchee.cli.command.api.Command;
+import org.apache.batchee.cli.command.api.UserCommand;
 
 @Command(name = "user1")
 public class User1 implements UserCommand {

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/a5191447/tools/cli/src/test/java/org/apache/batchee/cli/command/User2.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/test/java/org/apache/batchee/cli/command/User2.java b/tools/cli/src/test/java/org/apache/batchee/cli/command/User2.java
index 773c2ae..616e07e 100644
--- a/tools/cli/src/test/java/org/apache/batchee/cli/command/User2.java
+++ b/tools/cli/src/test/java/org/apache/batchee/cli/command/User2.java
@@ -16,7 +16,8 @@
  */
 package org.apache.batchee.cli.command;
 
-import io.airlift.airline.Command;
+import org.apache.batchee.cli.command.api.Command;
+import org.apache.batchee.cli.command.api.UserCommand;
 
 @Command(name = "user2")
 public class User2 implements UserCommand {