You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gn...@apache.org on 2014/03/05 16:08:27 UTC

[05/10] git commit: [KARAF-2805] Clean console and commands model

[KARAF-2805] Clean console and commands model

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

Branch: refs/heads/master
Commit: e7d23bef3856780ab07aa813f8feea56572eb2f2
Parents: bd56b49
Author: Guillaume Nodet <gn...@gmail.com>
Authored: Wed Mar 5 14:39:38 2014 +0100
Committer: Guillaume Nodet <gn...@gmail.com>
Committed: Wed Mar 5 15:10:37 2014 +0100

----------------------------------------------------------------------
 pom.xml                                         |   5 +
 shell/core/NOTICE                               |  71 +++
 shell/core/pom.xml                              | 179 ++++++
 .../karaf/shell/console/branding-ssh.properties |  34 ++
 .../karaf/shell/console/branding.properties     |  33 ++
 .../apache/karaf/shell/api/action/Action.java   |  56 ++
 .../apache/karaf/shell/api/action/Argument.java |  73 +++
 .../apache/karaf/shell/api/action/Command.java  |  61 ++
 .../karaf/shell/api/action/Completion.java      |  59 ++
 .../apache/karaf/shell/api/action/Option.java   |  71 +++
 .../shell/api/action/lifecycle/Destroy.java     |  34 ++
 .../karaf/shell/api/action/lifecycle/Init.java  |  34 ++
 .../shell/api/action/lifecycle/Manager.java     |  49 ++
 .../shell/api/action/lifecycle/Reference.java   |  39 ++
 .../shell/api/action/lifecycle/Service.java     |  36 ++
 .../apache/karaf/shell/api/console/Command.java |  51 ++
 .../karaf/shell/api/console/CommandLine.java    |  61 ++
 .../karaf/shell/api/console/Completer.java      |  38 ++
 .../karaf/shell/api/console/Function.java       |  38 ++
 .../apache/karaf/shell/api/console/History.java |  45 ++
 .../karaf/shell/api/console/Registry.java       |  77 +++
 .../apache/karaf/shell/api/console/Session.java | 153 +++++
 .../karaf/shell/api/console/SessionFactory.java |  65 +++
 .../karaf/shell/api/console/Terminal.java       |  51 ++
 .../impl/action/command/ActionCommand.java      | 109 ++++
 .../impl/action/command/ArgumentCompleter.java  | 385 ++++++++++++
 .../action/command/DefaultActionPreparator.java | 481 +++++++++++++++
 .../shell/impl/action/command/HelpOption.java   |  57 ++
 .../shell/impl/action/command/ManagerImpl.java  | 168 ++++++
 .../shell/impl/action/osgi/CommandExtender.java |  94 +++
 .../impl/action/osgi/CommandExtension.java      | 202 +++++++
 .../impl/action/osgi/MultiServiceTracker.java   | 106 ++++
 .../shell/impl/action/osgi/RegistryImpl.java    | 159 +++++
 .../shell/impl/action/osgi/Satisfiable.java     |  30 +
 .../impl/action/osgi/SingleServiceTracker.java  | 168 ++++++
 .../karaf/shell/impl/console/Branding.java      |  72 +++
 .../impl/console/CommandNamesCompleter.java     |  47 ++
 .../shell/impl/console/CommandWrapper.java      |  61 ++
 .../shell/impl/console/CommandsCompleter.java   | 256 ++++++++
 .../impl/console/CompleterAsCompletor.java      |  41 ++
 .../shell/impl/console/ConsoleSessionImpl.java  | 583 +++++++++++++++++++
 .../shell/impl/console/HeadlessSessionImpl.java | 146 +++++
 .../shell/impl/console/HistoryWrapper.java      |  48 ++
 .../karaf/shell/impl/console/JLineTerminal.java |  62 ++
 .../shell/impl/console/KarafFileHistory.java    |  64 ++
 .../karaf/shell/impl/console/KarafTerminal.java |  57 ++
 .../karaf/shell/impl/console/RegistryImpl.java  | 159 +++++
 .../shell/impl/console/SessionFactoryImpl.java  | 144 +++++
 .../shell/impl/console/TerminalFactory.java     |  47 ++
 .../impl/console/commands/ExitCommand.java      |  51 ++
 .../impl/console/commands/SubShellCommand.java  |  58 ++
 .../impl/console/commands/TopLevelCommand.java  |  86 +++
 .../commands/help/CommandListHelpProvider.java  | 117 ++++
 .../impl/console/commands/help/HelpCommand.java | 225 +++++++
 .../console/commands/help/HelpProvider.java     |  27 +
 .../commands/help/SimpleHelpProvider.java       |  47 ++
 .../help/SingleCommandHelpProvider.java         |  54 ++
 .../shell/impl/console/osgi/Activator.java      |  78 +++
 .../shell/impl/console/osgi/Converters.java     | 279 +++++++++
 .../shell/impl/console/osgi/DelayedStarted.java |  87 +++
 .../impl/console/osgi/LocalConsoleManager.java  | 133 +++++
 .../shell/impl/console/osgi/StreamWrapUtil.java |  91 +++
 .../console/osgi/secured/SecuredCommand.java    |  85 +++
 .../osgi/secured/SecuredSessionFactoryImpl.java | 239 ++++++++
 .../osgi/secured/SingleServiceTracker.java      | 171 ++++++
 .../impl/console/parsing/CommandLineImpl.java   |  91 +++
 .../shell/impl/console/parsing/Parser.java      | 396 +++++++++++++
 .../shell/impl/console/standalone/Main.java     | 278 +++++++++
 .../karaf/shell/support/CommandException.java   |  64 ++
 .../karaf/shell/support/MultiException.java     |  95 +++
 .../apache/karaf/shell/support/NameScoping.java |  79 +++
 .../apache/karaf/shell/support/ShellUtil.java   | 214 +++++++
 .../karaf/shell/support/ansi/SimpleAnsi.java    |  30 +
 .../support/completers/AggregateCompleter.java  |  96 +++
 .../completers/CommandNamesCompleter.java       |  27 +
 .../support/completers/CommandsCompleter.java   |  27 +
 .../shell/support/completers/FileCompleter.java | 147 +++++
 .../shell/support/completers/NullCompleter.java |  34 ++
 .../support/completers/StringsCompleter.java    | 113 ++++
 .../support/converter/DefaultConverter.java     | 403 +++++++++++++
 .../shell/support/converter/GenericType.java    | 195 +++++++
 .../shell/support/converter/ReifiedType.java    | 116 ++++
 .../karaf/shell/support/table/AnsiColumn.java   |  54 ++
 .../apache/karaf/shell/support/table/Col.java   | 113 ++++
 .../karaf/shell/support/table/HAlign.java       |  71 +++
 .../apache/karaf/shell/support/table/Row.java   |  69 +++
 .../karaf/shell/support/table/ShellTable.java   | 143 +++++
 .../karaf/shell/support/table/StringUtil.java   |  48 ++
 .../src/main/resources/OSGI-INF/bundle.info     |  16 +
 shell/pom.xml                                   |   1 +
 90 files changed, 9907 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index f505153..032442a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -659,6 +659,11 @@
                 <artifactId>org.apache.karaf.shell.table</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <dependency>
+                <groupId>org.apache.karaf.shell</groupId>
+                <artifactId>org.apache.karaf.shell.core</artifactId>
+                <version>${project.version}</version>
+            </dependency>
 
             <dependency>
                 <groupId>org.apache.karaf.jaas</groupId>

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/NOTICE
----------------------------------------------------------------------
diff --git a/shell/core/NOTICE b/shell/core/NOTICE
new file mode 100644
index 0000000..b70f1f9
--- /dev/null
+++ b/shell/core/NOTICE
@@ -0,0 +1,71 @@
+Apache Karaf
+Copyright 2010-2014 The Apache Software Foundation
+
+
+I. Included Software
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2010).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+OW2 (http://www.ow2.org/).
+Licensed under the BSD License.
+
+This product includes software developed at
+OPS4J (http://www.ops4j.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+Eclipse Foundation (http://www.eclipse.org/).
+Licensed under the EPL.
+
+This product includes software written by
+Antony Lesuisse.
+Licensed under Public Domain.
+
+
+II. Used Software
+
+This product uses software developed at
+FUSE Source (http://www.fusesource.org/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+AOP Alliance (http://aopalliance.sourceforge.net/).
+Licensed under the Public Domain.
+
+This product uses software developed at
+Tanuki Software (http://www.tanukisoftware.com/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+Jasypt (http://jasypt.sourceforge.net/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+JLine (http://jline.sourceforge.net).
+Licensed under the BSD License.
+
+This product uses software developed at
+SLF4J (http://www.slf4j.org/).
+Licensed under the MIT License.
+
+This product uses software developed at
+SpringSource (http://www.springsource.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software from http://www.json.org.
+Copyright (c) 2002 JSON.org
+
+
+III. License Summary
+- Apache License 2.0
+- BSD License
+- EPL License
+- MIT License

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/pom.xml
----------------------------------------------------------------------
diff --git a/shell/core/pom.xml b/shell/core/pom.xml
new file mode 100644
index 0000000..c41b0b2
--- /dev/null
+++ b/shell/core/pom.xml
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <!--
+
+        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.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.karaf.shell</groupId>
+        <artifactId>shell</artifactId>
+        <version>3.1.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>org.apache.karaf.shell.core</artifactId>
+    <packaging>bundle</packaging>
+    <name>Apache Karaf :: Shell :: Core</name>
+    <description>This bundle provides OSGi shell integration and console support.</description>
+
+    <properties>
+        <appendedResourcesDirectory>${basedir}/../../etc/appended-resources</appendedResourcesDirectory>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>jline</groupId>
+            <artifactId>jline</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.karaf.jaas</groupId>
+            <artifactId>org.apache.karaf.jaas.modules</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.karaf.jaas</groupId>
+            <artifactId>org.apache.karaf.jaas.boot</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.aries.blueprint</groupId>
+            <artifactId>org.apache.aries.blueprint.api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.blueprint</groupId>
+            <artifactId>org.apache.aries.blueprint.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.gogo.runtime</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.utils</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.karaf</groupId>
+            <artifactId>org.apache.karaf.util</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.karaf.service</groupId>
+            <artifactId>org.apache.karaf.service.guard</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>${project.basedir}/../../client/src/main/key</directory>
+                <filtering>false</filtering>
+            </resource>
+            <resource>
+                <directory>${project.basedir}/src/main/resources</directory>
+                <includes>
+                    <include>**/*</include>
+                </includes>
+            </resource>
+            <resource>
+                <directory>${project.basedir}/src/main/resources</directory>
+                <filtering>true</filtering>
+                <includes>
+                    <include>**/*.info</include>
+                </includes>
+            </resource>
+            <resource>
+                <directory>${project.basedir}/src/main/filtered-resources</directory>
+                <filtering>true</filtering>
+                <includes>
+                    <include>**/*</include>
+                </includes>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>Main</mainClass>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Import-Package>
+                            org.osgi.service.event;resolution:=optional,
+                            org.apache.karaf.branding;resolution:=optional,
+                            *
+                        </Import-Package>
+                        <Export-Package>
+                            org.apache.karaf.shell.api.*;version=${project.version},
+                            org.apache.karaf.shell.support.*;version=${project.version},
+                        </Export-Package>
+                        <Private-Package>
+                            org.apache.karaf.service.guard.tools,
+                            org.apache.karaf.shell.impl.*,
+                            org.apache.karaf.util.properties,
+                            org.apache.felix.utils.extender,
+                            org.apache.felix.utils.manifest,
+                            org.apache.felix.gogo.api,
+                            org.apache.felix.gogo.runtime,
+                            org.apache.felix.gogo.runtime.threadio,
+                            org.apache.felix.service.command,
+                            org.apache.felix.service.threadio,
+                        </Private-Package>
+                        <Bundle-Activator>
+                            org.apache.karaf.shell.impl.console.osgi.Activator
+                        </Bundle-Activator>
+                        <Main-Class>
+                            org.apache.karaf.shell.impl.console.standalone.Main
+                        </Main-Class>
+                    </instructions>
+                    <unpackBundle>true</unpackBundle>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/filtered-resources/org/apache/karaf/shell/console/branding-ssh.properties
----------------------------------------------------------------------
diff --git a/shell/core/src/main/filtered-resources/org/apache/karaf/shell/console/branding-ssh.properties b/shell/core/src/main/filtered-resources/org/apache/karaf/shell/console/branding-ssh.properties
new file mode 100644
index 0000000..978484b
--- /dev/null
+++ b/shell/core/src/main/filtered-resources/org/apache/karaf/shell/console/branding-ssh.properties
@@ -0,0 +1,34 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License.  You may obtain a copy of the License at
+##
+##  http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied.  See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+welcome = \
+\u001B[36m        __ __                  ____      \u001B[0m\r\n\
+\u001B[36m       / //_/____ __________ _/ __/      \u001B[0m\r\n\
+\u001B[36m      / ,<  / __ `/ ___/ __ `/ /_        \u001B[0m\r\n\
+\u001B[36m     / /| |/ /_/ / /  / /_/ / __/        \u001B[0m\r\n\
+\u001B[36m    /_/ |_|\\__,_/_/   \\__,_/_/         \u001B[0m\r\n\
+\r\n\
+\u001B[1m  Apache Karaf\u001B[0m (${project.version})\r\n\
+\r\n\
+Hit '\u001B[1m<tab>\u001B[0m' for a list of available commands\r\n\
+   and '\u001B[1m[cmd] --help\u001B[0m' for help on a specific command.\r\n\
+Hit '\u001B[1msystem:shutdown\u001B[0m' to shutdown Karaf.\r\n\
+Hit '\u001B[1m<ctrl-d>\u001B[0m' or type '\u001B[1mlogout\u001B[0m' to disconnect shell from current session.\r\n
+
+

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/filtered-resources/org/apache/karaf/shell/console/branding.properties
----------------------------------------------------------------------
diff --git a/shell/core/src/main/filtered-resources/org/apache/karaf/shell/console/branding.properties b/shell/core/src/main/filtered-resources/org/apache/karaf/shell/console/branding.properties
new file mode 100644
index 0000000..3fbcb0a
--- /dev/null
+++ b/shell/core/src/main/filtered-resources/org/apache/karaf/shell/console/branding.properties
@@ -0,0 +1,33 @@
+##
+## 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.
+##
+
+welcome = \
+\u001B[36m        __ __                  ____      \u001B[0m\r\n\
+\u001B[36m       / //_/____ __________ _/ __/      \u001B[0m\r\n\
+\u001B[36m      / ,<  / __ `/ ___/ __ `/ /_        \u001B[0m\r\n\
+\u001B[36m     / /| |/ /_/ / /  / /_/ / __/        \u001B[0m\r\n\
+\u001B[36m    /_/ |_|\\__,_/_/   \\__,_/_/         \u001B[0m\r\n\
+\r\n\
+\u001B[1m  Apache Karaf\u001B[0m (${project.version})\r\n\
+\r\n\
+Hit '\u001B[1m<tab>\u001B[0m' for a list of available commands\r\n\
+   and '\u001B[1m[cmd] --help\u001B[0m' for help on a specific command.\r\n\
+Hit '\u001B[1m<ctrl-d>\u001B[0m' or type '\u001B[1msystem:shutdown\u001B[0m' or '\u001B[1mlogout\u001B[0m' to shutdown Karaf.\r\n
+
+

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/action/Action.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/action/Action.java b/shell/core/src/main/java/org/apache/karaf/shell/api/action/Action.java
new file mode 100644
index 0000000..955c837
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/action/Action.java
@@ -0,0 +1,56 @@
+/*
+ * 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.karaf.shell.api.action;
+
+/**
+ * An action is the default implementation of the commands in karaf.
+ * In OSGi, Actions are discovered using an extender and a new instance
+ * of the class is created when the command is invoked, so that the
+ * implementation does not need to be thread safe.
+ *
+ * Before the call to the execute method the action is checked for
+ * fields annotated with @Reference and injected with services coming
+ * from the SessionFactory's Registry or from the OSGi registry.
+ * Methods annotated with @Init are then called.  The next step is to
+ * inject command line parameters into fields annotated with @Option
+ * and @Argument and then call the execute method.
+ * 
+ * Any class implementing Action must have a no argument constructor. This
+ * is necessary so the help generator can instantiate the class and get the 
+ * default values.
+ *
+ * In order to make commands available from the non-OSGi shell,
+ * the commands must be listed in a file available at
+ * META-INF/services/org/apache/karaf/shell/commmands.
+ *
+ * @see org.apache.karaf.shell.api.action.Command
+ * @see org.apache.karaf.shell.api.action.lifecycle.Service
+ */
+public interface Action {
+
+    /**
+     * Execute the action which has been injected with services from the
+     * registry, options and arguments from the command line.
+     *
+     * @return <code>null</code> or the result of the action execution
+     * @throws Exception
+     */
+    Object execute() throws Exception;
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/action/Argument.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/action/Argument.java b/shell/core/src/main/java/org/apache/karaf/shell/api/action/Argument.java
new file mode 100644
index 0000000..8cb1e51
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/action/Argument.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.shell.api.action;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Represents a positional argument on a command line (as opposed to an optional named {@link Option}
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD})
+public @interface Argument
+{
+    public static final String DEFAULT_STRING= "DEFAULT";
+
+    String DEFAULT = "##default";
+
+    /**
+     * Name of the argument.
+     * By default, the field name will be used.
+     */
+    String name() default DEFAULT;
+
+    /**
+     * A textual description of the argument.
+     */
+    String description() default "";
+
+    /**
+     * Whether this argument is mandatory or not.
+     */
+    boolean required() default false;
+
+    /**
+     * Position of the argument in the command line.
+     * When using multiple arguments, the indices must be
+     * starting from 0 and incrementing without any holes.
+     */
+    int index() default 0;
+
+    /**
+     * The last argument can be multi-valued in which case
+     * the field type must be a List.
+     */
+    boolean multiValued() default false;
+
+    /**
+     * The generated help displays default values for arguments.
+     * In case the value displayed in the help to the user should
+     * be different that the default value of the field, one
+     * can use this property to specify the value to display.
+     */
+    String valueToShowInHelp() default DEFAULT_STRING;
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/action/Command.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/action/Command.java b/shell/core/src/main/java/org/apache/karaf/shell/api/action/Command.java
new file mode 100644
index 0000000..7622b33
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/action/Command.java
@@ -0,0 +1,61 @@
+/*
+ * 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.karaf.shell.api.action;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Used to denote a class represents a command which is executable
+ * within a shell/scope or as a command line process.
+ *
+ * All classes annotated with @Command should implement the
+ * {@link org.apache.karaf.shell.api.action.Action} interface.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface Command
+{
+    /**
+     * Returns the scope or sub shell of the command
+     */
+    String scope();
+
+    /**
+     * Returns the name of the command if used inside a shell
+     */
+    String name();
+
+    /**
+     * Returns the description of the command which is used to generate command line help
+     */
+    String description() default "";
+
+    /**
+     * Returns a detailed description of the command.
+     * This description will be shown in the help for the command.
+     * Longer descriptions can be externalized using a
+     * <code>classpath:[location]</code> url, in which case the
+     * descrition will be loaded from the bundle at the given location,
+     * relatively to the implementation of the command.
+     */
+    String detailedDescription() default "";
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/action/Completion.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/action/Completion.java b/shell/core/src/main/java/org/apache/karaf/shell/api/action/Completion.java
new file mode 100644
index 0000000..d9a3dca
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/action/Completion.java
@@ -0,0 +1,59 @@
+/*
+ * 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.karaf.shell.api.action;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The @Completion annotation can be used on a field annotated with
+ * {@link Option} or {@link Argument} to specify the completion
+ * method to use for this field.
+ *
+ * @see org.apache.karaf.shell.api.console.Completer
+ * @see org.apache.karaf.shell.support.completers.StringsCompleter
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD})
+public @interface Completion {
+
+    /**
+     * The completer class to use for this field.
+     * The console registry will be used to look for
+     * a completer of this class.
+     *
+     * A special case for simple static completions is to use
+     * {@link org.apache.karaf.shell.support.completers.StringsCompleter},
+     * in which case, the <code>values</code> property will be used
+     * as the list of possible completions.
+     */
+    Class<?> value();
+
+    /**
+     * When using a static completer, returns the possible values.
+     */
+    String[] values() default { };
+
+    /**
+     * When using a static completer, indicates if completion
+     * should be done case sensitive or not.
+     */
+    boolean caseSensitive() default false;
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/action/Option.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/action/Option.java b/shell/core/src/main/java/org/apache/karaf/shell/api/action/Option.java
new file mode 100644
index 0000000..97e7557
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/action/Option.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.karaf.shell.api.action;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Used to mark an optional named command line option who's name typically starts with "--" or "-".
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD})
+public @interface Option
+{
+    public static final String DEFAULT_STRING= "DEFAULT";
+
+    /**
+     * The name of this option.  Usually starting with a '-'.
+     */
+    String name();
+
+    /**
+     * Specify a list of aliases for this option.
+     * Useful when using an option with short or long names.
+     */
+    String[] aliases() default {};
+
+    /**
+     * A textual description of the option.
+     */
+    String description() default "";
+
+    /**
+     * Whether this argument is mandatory or not.
+     */
+    boolean required() default false;
+
+    /**
+     * The last argument can be multi-valued in which case
+     * the field type must be a List.  On the command line,
+     * multi-valued options are used with specifying the option
+     * multiple times with different values.
+     */
+    boolean multiValued() default false;
+
+    /**
+     * The generated help displays default values for arguments.
+     * In case the value displayed in the help to the user should
+     * be different that the default value of the field, one
+     * can use this property to specify the value to display.
+     */
+    String valueToShowInHelp() default DEFAULT_STRING;
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Destroy.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Destroy.java b/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Destroy.java
new file mode 100644
index 0000000..86199c0
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Destroy.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.shell.api.action.lifecycle;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A class annotated with {@link @Service} can have a method
+ * annotation with <code>@Destroy</code> in which case the annotated
+ * method will be called when the object is destroyed.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface Destroy {
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Init.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Init.java b/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Init.java
new file mode 100644
index 0000000..d0cd3c0
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Init.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.shell.api.action.lifecycle;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A class annotated with {@link @Service} can have a method
+ * annotation with <code>@Init</code> in which case the annotated
+ * method will be called after a successful injection.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface Init {
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Manager.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Manager.java b/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Manager.java
new file mode 100644
index 0000000..2b26e81
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Manager.java
@@ -0,0 +1,49 @@
+/*
+ * 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.karaf.shell.api.action.lifecycle;
+
+/**
+ * The <code>Manager</code> service can be used to programmatically
+ * register {@link org.apache.karaf.shell.api.action.Action}s or
+ * {@link org.apache.karaf.shell.api.console.Completer}s.
+ *
+ * Registered objects must be annotated with the {@link Service} annotation.
+ *
+ * Objects will be registered in the {@link org.apache.karaf.shell.api.console.Registry}
+ * associated with this <code>Manager</code>.
+ *
+ * @see org.apache.karaf.shell.api.console.Registry
+ * @see org.apache.karaf.shell.api.action.lifecycle.Service
+ */
+public interface Manager {
+
+    /**
+     * Register a service.
+     * If the given class is an {@link org.apache.karaf.shell.api.action.Action},
+     * a {@link org.apache.karaf.shell.api.console.Command} will be created and registered,
+     * else, an instance of the class will be created, injected and registered.
+     */
+    void register(Class<?> clazz);
+
+    /**
+     * Unregister a previously registered class.
+     */
+    void unregister(Class<?> clazz);
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Reference.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Reference.java b/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Reference.java
new file mode 100644
index 0000000..4d8a73c
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Reference.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.shell.api.action.lifecycle;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A class annotated with {@link @Service} can have fields
+ * annotated with <code>@Service</code> in which case matching
+ * services will be retrieved from the
+ * {@link org.apache.karaf.shell.api.console.Registry} and
+ * injected.
+ *
+ * If a field has a {@link java.util.List} type, it will be injected
+ * with a list containing all matching services.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD})
+public @interface Reference {
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Service.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Service.java b/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Service.java
new file mode 100644
index 0000000..d5d62a3
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Service.java
@@ -0,0 +1,36 @@
+/*
+ * 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.karaf.shell.api.action.lifecycle;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Classes that need to be managed must be annotated
+ * with <code>@Service</code> annotation.
+ *
+ * Services can either implement {@link org.apache.karaf.shell.api.action.Action}
+ * or {@link org.apache.karaf.shell.api.console.Completer}.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface Service {
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/console/Command.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/console/Command.java b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Command.java
new file mode 100644
index 0000000..4bb1db0
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Command.java
@@ -0,0 +1,51 @@
+/*
+ * 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.karaf.shell.api.console;
+
+/**
+ * A <code>Command</code> is a named
+ * {@link org.apache.karaf.shell.api.console.Function}
+ * which also provides completion.
+ */
+public interface Command extends Function {
+
+    /**
+     * Retrieve the scope of this command.
+     */
+    String getScope();
+
+    /**
+     * Retrieve the name of this command.
+     */
+    String getName();
+
+    /**
+     * Retrieve the description of this command.
+     * This short command description will be printed
+     * when using the <code>help</code> command.
+     */
+    String getDescription();
+
+    /**
+     * Retrieve the completer associated with this command.
+     *
+     * @param scoped whether the command is invoked from a subshell or not
+     * @return the {@link Completer} to use
+     */
+    Completer getCompleter(boolean scoped);
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/console/CommandLine.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/console/CommandLine.java b/shell/core/src/main/java/org/apache/karaf/shell/api/console/CommandLine.java
new file mode 100644
index 0000000..2ed7697
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/console/CommandLine.java
@@ -0,0 +1,61 @@
+/*
+ * 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.karaf.shell.api.console;
+
+/**
+ * A <code>CommandLine</code> object will be created and
+ * given the {@link org.apache.karaf.shell.api.console.Completer}s to ease
+ * their work.  Arguments are separated and the cursor position within the
+ * current argument is given.
+ */
+public interface CommandLine {
+
+    /**
+     * Retrieve the argument index for the cursor position
+     */
+    int getCursorArgumentIndex();
+
+    /**
+     * Retrieve the argument for the cursor position
+     */
+    String getCursorArgument();
+
+    /**
+     * Retrieve the position of the cursor within the argument
+     */
+    int getArgumentPosition();
+
+    /**
+     * List of arguments on the current command.
+     * If the command line contains multiple commands, only the command corresponding
+     * to the cursor position is available.
+     */
+    String[] getArguments();
+
+    /**
+     * Retrieve the position of the cursor within the command line.
+     */
+    int getBufferPosition();
+
+    /**
+     * Retrieve the full buffer.
+     */
+    String getBuffer();
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/console/Completer.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/console/Completer.java b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Completer.java
new file mode 100644
index 0000000..4f9f3d1
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Completer.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.karaf.shell.api.console;
+
+import java.util.List;
+
+/**
+ * A <code>Completer</code> is used by the console to complete the command line.
+ */
+public interface Completer {
+
+    /**
+     * populate possible completion candidates.
+     *
+     * @param session the current {@link Session}
+     * @param commandLine the pre-parsed {@link CommandLine}
+     * @param candidates a list to fill with possible completion candidates
+     * @return the index of the{@link CommandLine} for which the completion will be relative
+     */
+    int complete(Session session, CommandLine commandLine, List<String> candidates);
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/console/Function.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/console/Function.java b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Function.java
new file mode 100644
index 0000000..209039f
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Function.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.karaf.shell.api.console;
+
+import java.util.List;
+
+/**
+ * This interface represents some code that can be executed in the
+ * {@link Session}.
+ */
+public interface Function {
+
+    /**
+     * Execute this function within the given Session and with the given
+     * arguments.
+     *
+     * @param session the current session
+     * @param arguments the arguments of this function
+     * @return the result
+     * @throws Exception if any exception occurs
+     */
+    Object execute(Session session, List<Object> arguments) throws Exception;
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/console/History.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/console/History.java b/shell/core/src/main/java/org/apache/karaf/shell/api/console/History.java
new file mode 100644
index 0000000..10b1b11
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/console/History.java
@@ -0,0 +1,45 @@
+/*
+ * 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.karaf.shell.api.console;
+
+/**
+ * Session history.
+ */
+public interface History {
+
+    /**
+     * First available index.
+     */
+    int first();
+
+    /**
+     * Last available index.
+     */
+    int last();
+
+    /**
+     * Command at the given index.
+     * Indices can range from <code>first()</code> to <code>last()</code>.
+     */
+    CharSequence get(int index);
+
+    /**
+     * Clear the history.
+     */
+    void clear();
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/console/Registry.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/console/Registry.java b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Registry.java
new file mode 100644
index 0000000..b768d69
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Registry.java
@@ -0,0 +1,77 @@
+/*
+ * 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.karaf.shell.api.console;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+
+/**
+ * Service registry.
+ *
+ * The registry can be used to register various services used during injection along
+ * with {@link Command}s.
+ *
+ * @see org.apache.karaf.shell.api.console.SessionFactory
+ * @see org.apache.karaf.shell.api.console.Session
+ */
+public interface Registry {
+
+    /**
+     * Return a list of available commands.
+     */
+    List<Command> getCommands();
+
+    /**
+     * Register a delayed service (or factory).
+     * In cases where instances must be created for each injection,
+     * a {@link Callable} can be registered and each injection will
+     * call it to obtain the actual service implementation.
+     *
+     * @param factory
+     * @param clazz
+     * @param <T>
+     */
+    <T> void register(Callable<T> factory, Class<T> clazz);
+
+    /**
+     * Register a service.
+     */
+    void register(Object service);
+
+    /**
+     * Unregister a service.
+     * If the registration has been done using a factory, the same
+     * factory should be used to unregister.
+     */
+    void unregister(Object service);
+
+    /**
+     * Obtain a service implementing the given class.
+     */
+    <T> T getService(Class<T> clazz);
+
+    /**
+     * Obtain a list of services implementing the given class.
+     */
+    <T> List<T> getServices(Class<T> clazz);
+
+    /**
+     * Check whether the registry has a service of the given class.
+     */
+    boolean hasService(Class<?> clazz);
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/console/Session.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/console/Session.java b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Session.java
new file mode 100644
index 0000000..cf7f795
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Session.java
@@ -0,0 +1,153 @@
+/*
+ * 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.karaf.shell.api.console;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+
+/**
+ * A <code>Session</code> can be used to execute commands.
+ *
+ * The {@link org.apache.karaf.shell.api.console.Registry} associated
+ * with this <code>Session</code> will contain: <ul>
+ *     <li>{@link SessionFactory}</li>
+ *     <li>{@link Command}s</li>
+ *     <li>{@link Session}</li>
+ *     <li>{@link Registry}</li>
+ *     <li>{@link History}</li>
+ *     <li>{@link Terminal}</li>
+ * </ul>
+ */
+public interface Session extends Runnable {
+
+    //
+    // Session properties
+    //
+    // Property names starting with "karaf." are reserved for karaf
+    //
+
+    String SCOPE = "SCOPE";
+    String SUBSHELL = "SUBSHELL";
+
+    String PRINT_STACK_TRACES = "karaf.printStackTraces";
+    String LAST_EXCEPTION = "karaf.lastException";
+    String IGNORE_INTERRUPTS = "karaf.ignoreInterrupts";
+    String COMPLETION_MODE = "karaf.completionMode";
+
+    String COMPLETION_MODE_GLOBAL = "global";
+    String COMPLETION_MODE_SUBSHELL = "subshell";
+    String COMPLETION_MODE_FIRST = "first";
+
+    String SCOPE_GLOBAL = "*";
+
+    /**
+     * Execute a program in this session.
+     *
+     * @param commandline
+     * @return the result of the execution
+     */
+    Object execute(CharSequence commandline) throws Exception;
+
+    /**
+     * Get the value of a variable.
+     *
+     * @param name
+     * @return
+     */
+    Object get(String name);
+
+    /**
+     * Set the value of a variable.
+     *
+     * @param name  Name of the variable.
+     * @param value Value of the variable
+     */
+    void put(String name, Object value);
+
+    /**
+     * Return the input stream that is the first of the pipeline. This stream is
+     * sometimes necessary to communicate directly to the end user. For example,
+     * a "less" or "more" command needs direct input from the keyboard to
+     * control the paging.
+     *
+     * @return InputStream used closest to the user or null if input is from a
+     *         file.
+     */
+    InputStream getKeyboard();
+
+    /**
+     * Return the PrintStream for the console. This must always be the stream
+     * "closest" to the user. This stream can be used to post messages that
+     * bypass the piping. If the output is piped to a file, then the object
+     * returned must be null.
+     *
+     * @return
+     */
+    PrintStream getConsole();
+
+    /**
+     * Prompt the user for a line.
+     *
+     * @param prompt
+     * @param mask
+     * @return
+     * @throws java.io.IOException
+     */
+    String readLine(String prompt, final Character mask) throws IOException;
+
+    /**
+     * Retrieve the {@link org.apache.karaf.shell.api.console.Terminal} associated
+     * with this <code>Session</code> or <code>null</code> if this <code>Session</code>
+     * is headless.
+     */
+    Terminal getTerminal();
+
+    /**
+     * Retrieve the {@link org.apache.karaf.shell.api.console.History} associated
+     * with this <code>Session</code> or <code>null</code> if this <code>Session</code>
+     * is headless.
+     */
+    History getHistory();
+
+    /**
+     * Retrieve the {@link org.apache.karaf.shell.api.console.Registry} associated
+     * with this <code>Session</code>.
+     */
+    Registry getRegistry();
+
+    /**
+     * Retrieve the {@link org.apache.karaf.shell.api.console.SessionFactory} associated
+     * with this <code>Session</code>.
+     */
+    SessionFactory getFactory();
+
+    /**
+     * Resolve a command name.  If the command name has no specified scope, the fully
+     * qualified command name will be returned, depending on the scopes and current
+     * subshell.
+     */
+    String resolveCommand(String name);
+
+    /**
+     * Close this session. After the session is closed, it will throw
+     * IllegalStateException when it is used.
+     */
+    void close();
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/console/SessionFactory.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/console/SessionFactory.java b/shell/core/src/main/java/org/apache/karaf/shell/api/console/SessionFactory.java
new file mode 100644
index 0000000..7b02fd6
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/console/SessionFactory.java
@@ -0,0 +1,65 @@
+/*
+ * 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.karaf.shell.api.console;
+
+import java.io.InputStream;
+import java.io.PrintStream;
+
+/**
+ * The <code>SessionFactory</code> can be used to create
+ * {@link Session} to execute commands.
+ *
+ * The {@link org.apache.karaf.shell.api.console.Registry} associated
+ * with this <code>SessionFactory</code> will contain: <ul>
+ *     <li>{@link SessionFactory}</li>
+ *     <li>{@link Registry}</li>
+ *     <li>{@link Command}s</li>
+ * </ul>
+ */
+public interface SessionFactory {
+
+    /**
+     * Retrieve the {@link Registry} used by this <code>SessionFactory</code>.
+     */
+    Registry getRegistry();
+
+    /**
+     * Create new interactive session.
+     *
+     * @param in the input stream, can be <code>null</code> if the session is only used to execute a command using {@link Session#execute(CharSequence)}
+     * @param out the output stream
+     * @param err the error stream
+     * @param term the {@link Terminal} to use, may be <code>null</code>
+     * @param encoding the encoding to use for the input stream, may be <code>null</code>
+     * @param closeCallback a callback to be called when the session is closed, may be <code>null</code>
+     * @return the new session
+     */
+    Session create(InputStream in, PrintStream out, PrintStream err, Terminal term, String encoding, Runnable closeCallback);
+
+    /**
+     * Create a new headless session.
+     * Headless session can only be used to execute commands, so that
+     * {@link org.apache.karaf.shell.api.console.Session#run()} can not be used.
+     *
+     * @param in the input stream, can be <code>null</code> if the session is only used to execute a command using {@link Session#execute(CharSequence)}
+     * @param out the output stream
+     * @param err the error stream
+     * @return the new session
+     */
+    Session create(InputStream in, PrintStream out, PrintStream err);
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/console/Terminal.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/console/Terminal.java b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Terminal.java
new file mode 100644
index 0000000..3549ec7
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Terminal.java
@@ -0,0 +1,51 @@
+/*
+ * 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.karaf.shell.api.console;
+
+/**
+ * Session terminal.
+ */
+public interface Terminal {
+
+    /**
+     * Width of the terminal.
+     */
+    int getWidth();
+
+    /**
+     * Height of the terminal.
+     */
+    int getHeight();
+
+    /**
+     * Whether ansi sequences are supported or not.
+     */
+    boolean isAnsiSupported();
+
+    /**
+     * Whether echo is enabled or not.
+     */
+    boolean isEchoEnabled();
+
+    /**
+     * Enable or disable echo.
+     */
+    void setEchoEnabled(boolean enabled);
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ActionCommand.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ActionCommand.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ActionCommand.java
new file mode 100644
index 0000000..4cd58ca
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ActionCommand.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.shell.impl.action.command;
+
+import java.util.List;
+
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.console.CommandLine;
+import org.apache.karaf.shell.api.console.Completer;
+import org.apache.karaf.shell.api.console.Session;
+
+public class ActionCommand implements org.apache.karaf.shell.api.console.Command {
+
+    private final ManagerImpl manager;
+    private final Class<? extends Action> actionClass;
+
+    public ActionCommand(ManagerImpl manager, Class<? extends Action> actionClass) {
+        this.manager = manager;
+        this.actionClass = actionClass;
+    }
+
+    public Class<? extends Action> getActionClass() {
+        return actionClass;
+    }
+
+    @Override
+    public String getScope() {
+        return actionClass.getAnnotation(Command.class).scope();
+    }
+
+    @Override
+    public String getName() {
+        return actionClass.getAnnotation(Command.class).name();
+    }
+
+    @Override
+    public String getDescription() {
+        return actionClass.getAnnotation(Command.class).description();
+    }
+
+    @Override
+    public Completer getCompleter(boolean scoped) {
+        return new ArgumentCompleter(this, scoped);
+    }
+
+    protected Completer getCompleter(Class<?> clazz) {
+        return new DelayedCompleter(clazz);
+    }
+
+    @Override
+    public Object execute(Session session, List<Object> arguments) throws Exception {
+        Action action = createNewAction(session);
+        try {
+            if (new DefaultActionPreparator().prepare(action, session, arguments)) {
+                return action.execute();
+            }
+        } finally {
+            releaseAction(action);
+        }
+        return null;
+    }
+
+    protected Action createNewAction(Session session) {
+        try {
+            return manager.instantiate(actionClass, session.getRegistry());
+        } catch (Exception e) {
+            throw new RuntimeException("Unable to creation command action " + actionClass.getName(), e);
+        }
+    }
+
+    protected void releaseAction(Action action) throws Exception {
+        manager.release(action);
+    }
+
+    public static class DelayedCompleter implements Completer {
+        private final Class<?> clazz;
+
+        public DelayedCompleter(Class<?> clazz) {
+            this.clazz = clazz;
+        }
+
+        @Override
+        public int complete(Session session, CommandLine commandLine, List<String> candidates) {
+            Object service = session.getRegistry().getService(clazz);
+            if (service instanceof Completer) {
+                return ((Completer) service).complete(session, commandLine, candidates);
+            }
+            return -1;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ArgumentCompleter.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ArgumentCompleter.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ArgumentCompleter.java
new file mode 100644
index 0000000..2868e6f
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ArgumentCompleter.java
@@ -0,0 +1,385 @@
+/*
+ * 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.karaf.shell.impl.action.command;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.Option;
+import org.apache.karaf.shell.api.console.CommandLine;
+import org.apache.karaf.shell.api.console.Completer;
+import org.apache.karaf.shell.api.console.Session;
+import org.apache.karaf.shell.support.completers.FileCompleter;
+import org.apache.karaf.shell.support.completers.NullCompleter;
+import org.apache.karaf.shell.support.completers.StringsCompleter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ArgumentCompleter implements Completer {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ArgumentCompleter.class);
+
+    final ActionCommand command;
+    final Completer commandCompleter;
+    final Completer optionsCompleter;
+    final List<Completer> argsCompleters;
+    final Map<String, Completer> optionalCompleters;
+    final Map<Option, Field> fields = new HashMap<Option, Field>();
+    final Map<String, Option> options = new HashMap<String, Option>();
+    final Map<Integer, Field> arguments = new HashMap<Integer, Field>();
+    boolean strict = true;
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public ArgumentCompleter(ActionCommand command, boolean scoped) {
+        this.command = command;
+        Class<?> actionClass = command.getActionClass();
+        // Command name completer
+        Command cmd = actionClass.getAnnotation(Command.class);
+        String[] names = scoped || Session.SCOPE_GLOBAL.equals(cmd.scope()) ? new String[] { cmd.name() } : new String[] { cmd.name(), cmd.scope() + ":" + cmd.name() };
+        commandCompleter = new StringsCompleter(names);
+        // Build options completer
+        for (Class<?> type = actionClass; type != null; type = type.getSuperclass()) {
+            for (Field field : type.getDeclaredFields()) {
+                Option option = field.getAnnotation(Option.class);
+                if (option != null) {
+                    fields.put(option, field);
+                    options.put(option.name(), option);
+                    String[] aliases = option.aliases();
+                    if (aliases != null) {
+                        for (String alias : aliases) {
+                            options.put(alias, option);
+                        }
+                    }
+                }
+                Argument argument = field.getAnnotation(Argument.class);
+                if (argument != null) {
+                    Integer key = argument.index();
+                    if (arguments.containsKey(key)) {
+                        LOGGER.warn("Duplicate @Argument annotations on class " + type.getName() + " for index: " + key + " see: " + field);
+                    } else {
+                        arguments.put(key, field);
+                    }
+                }
+            }
+        }
+        options.put(HelpOption.HELP.name(), HelpOption.HELP);
+
+        argsCompleters = new ArrayList<Completer>();
+        optionsCompleter = new StringsCompleter(options.keySet());
+
+        boolean multi = false;
+        for (int key = 0; key < arguments.size(); key++) {
+            Completer completer = null;
+            Field field = arguments.get(key);
+            if (field != null) {
+                Argument argument = field.getAnnotation(Argument.class);
+                multi = (argument != null && argument.multiValued());
+                Completion ann = field.getAnnotation(Completion.class);
+                if (ann != null) {
+                    Class<?> clazz = ann.value();
+                    String[] value = ann.values();
+                    if (clazz != null) {
+                        if (value.length > 0 && clazz == StringsCompleter.class) {
+                            completer = new StringsCompleter(value, ann.caseSensitive());
+                        } else {
+                            completer = command.getCompleter(clazz);
+                        }
+                    }
+                } else {
+                    completer = getDefaultCompleter(field);
+                }
+            }
+            if (completer == null) {
+                completer = NullCompleter.INSTANCE;
+            }
+            argsCompleters.add(completer);
+        }
+        if (argsCompleters.isEmpty() || !multi) {
+            argsCompleters.add(NullCompleter.INSTANCE);
+        }
+        optionalCompleters = new HashMap<String, Completer>();
+        for (Option option : fields.keySet()) {
+            Completer completer = null;
+            Field field = fields.get(option);
+            if (field != null) {
+                Completion ann = field.getAnnotation(Completion.class);
+                if (ann != null) {
+                    Class clazz = ann.value();
+                    String[] value = ann.values();
+                    if (clazz != null) {
+                        if (clazz == StringsCompleter.class) {
+                            completer = new StringsCompleter(value, ann.caseSensitive());
+                        } else {
+                            completer = command.getCompleter(clazz);
+                        }
+                    }
+                }
+            }
+            if (completer == null) {
+                completer = NullCompleter.INSTANCE;
+            }
+            optionalCompleters.put(option.name(), completer);
+            if (option.aliases() != null) {
+                for (String alias : option.aliases()) {
+                    optionalCompleters.put(alias, completer);
+                }
+            }
+        }
+    }
+
+    private Completer getDefaultCompleter(Field field) {
+        Completer completer = null;
+        Class<?> type = field.getType();
+        if (type.isAssignableFrom(File.class)) {
+            completer = new FileCompleter();
+        } else if (type.isAssignableFrom(Boolean.class) || type.isAssignableFrom(boolean.class)) {
+            completer = new StringsCompleter(new String[] {"false", "true"}, false);
+        } else if (type.isAssignableFrom(Enum.class)) {
+            Set<String> values = new HashSet<String>();
+            for (Object o : EnumSet.allOf((Class<Enum>) type)) {
+                values.add(o.toString());
+            }
+            completer = new StringsCompleter(values, false);
+        } else {
+            // TODO any other completers we can add?
+        }
+        return completer;
+    }
+
+    /**
+     *  If true, a completion at argument index N will only succeed
+     *  if all the completions from 0-(N-1) also succeed.
+     */
+    public void setStrict(final boolean strict) {
+        this.strict = strict;
+    }
+
+    /**
+     *  Returns whether a completion at argument index N will succees
+     *  if all the completions from arguments 0-(N-1) also succeed.
+     */
+    public boolean getStrict() {
+        return this.strict;
+    }
+
+    public int complete(Session session, final CommandLine list, final List<String> candidates) {
+        int argpos = list.getArgumentPosition();
+        int argIndex = list.getCursorArgumentIndex();
+
+        Completer comp = null;
+        String[] args = list.getArguments();
+        int index = 0;
+        // First argument is command name
+        if (index < argIndex) {
+            // Verify scope
+            if (!Session.SCOPE_GLOBAL.equals(command.getScope()) && !session.resolveCommand(args[index]).equals(command.getScope() + ":" + command.getName())) {
+                return -1;
+            }
+            // Verify command name
+            if (!verifyCompleter(session, commandCompleter, args[index])) {
+                return -1;
+            }
+            index++;
+        } else {
+            comp = commandCompleter;
+        }
+        // Now, check options
+        if (comp == null) {
+            while (index < argIndex && args[index].startsWith("-")) {
+                if (!verifyCompleter(session, optionsCompleter, args[index])) {
+                    return -1;
+                }
+                Option option = options.get(args[index]);
+                if (option == null) {
+                    return -1;
+                }
+                Field field = fields.get(option);
+                if (field != null && field.getType() != boolean.class && field.getType() != Boolean.class) {
+                    if (++index == argIndex) {
+                        comp = NullCompleter.INSTANCE;
+                    }
+                }
+                index++;
+            }
+            if (comp == null && index >= argIndex && index < args.length && args[index].startsWith("-")) {
+                comp = optionsCompleter;
+            }
+        }
+        //Now check for if last Option has a completer
+        int lastAgurmentIndex = argIndex - 1;
+        if (lastAgurmentIndex >= 1) {
+            Option lastOption = options.get(args[lastAgurmentIndex]);
+            if (lastOption != null) {
+                Field lastField = fields.get(lastOption);
+                if (lastField != null && lastField.getType() != boolean.class && lastField.getType() != Boolean.class) {
+                    Option option = lastField.getAnnotation(Option.class);
+                    if (option != null) {
+                        Completer optionValueCompleter = null;
+                        String name = option.name();
+                        if (name != null) {
+                            optionValueCompleter = optionalCompleters.get(name);
+                            if (optionValueCompleter == null) {
+                                String[] aliases = option.aliases();
+                                if (aliases.length > 0) {
+                                    for (int i = 0; i < aliases.length && optionValueCompleter == null; i++) {
+                                        optionValueCompleter = optionalCompleters.get(option.aliases()[i]);
+                                    }
+                                }
+                            }
+                        }
+                        if(optionValueCompleter != null) {
+                            comp = optionValueCompleter;
+                        }
+                    }
+                }
+            }
+        }
+
+        // Check arguments
+        if (comp == null) {
+            int indexArg = 0;
+            while (index < argIndex) {
+                Completer sub = argsCompleters.get(indexArg >= argsCompleters.size() ? argsCompleters.size() - 1 : indexArg);
+                if (!verifyCompleter(session, sub, args[index])) {
+                    return -1;
+                }
+                index++;
+                indexArg++;
+            }
+            comp = argsCompleters.get(indexArg >= argsCompleters.size() ? argsCompleters.size() - 1 : indexArg);
+        }
+
+        int ret = comp.complete(session, new ArgumentCommandLine(list.getCursorArgument(), argpos), candidates);
+
+        if (ret == -1) {
+            return -1;
+        }
+
+        int pos = ret + (list.getBufferPosition() - argpos);
+
+        /**
+         *  Special case: when completing in the middle of a line, and the
+         *  area under the cursor is a delimiter, then trim any delimiters
+         *  from the candidates, since we do not need to have an extra
+         *  delimiter.
+         *
+         *  E.g., if we have a completion for "foo", and we
+         *  enter "f bar" into the buffer, and move to after the "f"
+         *  and hit TAB, we want "foo bar" instead of "foo  bar".
+         */
+
+        String buffer = list.getBuffer();
+        int cursor = list.getBufferPosition();
+        if ((buffer != null) && (cursor != buffer.length()) && isDelimiter(buffer, cursor)) {
+            for (int i = 0; i < candidates.size(); i++) {
+                String val = candidates.get(i);
+
+                while ((val.length() > 0)
+                    && isDelimiter(val, val.length() - 1)) {
+                    val = val.substring(0, val.length() - 1);
+                }
+
+                candidates.set(i, val);
+            }
+        }
+
+        return pos;
+    }
+
+    protected boolean verifyCompleter(Session session, Completer completer, String argument) {
+        List<String> candidates = new ArrayList<String>();
+        return completer.complete(session, new ArgumentCommandLine(argument, argument.length()), candidates) != -1 && !candidates.isEmpty();
+    }
+
+    /**
+     *  Returns true if the specified character is a whitespace
+     *  parameter. Check to ensure that the character is not
+     *  escaped and returns true from
+     *  {@link #isDelimiterChar}.
+     *
+     *  @param  buffer the complete command buffer
+     *  @param  pos    the index of the character in the buffer
+     *  @return        true if the character should be a delimiter
+     */
+    public boolean isDelimiter(final String buffer, final int pos) {
+        return !isEscaped(buffer, pos) && isDelimiterChar(buffer, pos);
+    }
+
+    public boolean isEscaped(final String buffer, final int pos) {
+        return pos > 0 && buffer.charAt(pos) == '\\' && !isEscaped(buffer, pos - 1);
+    }
+
+    /**
+     *  The character is a delimiter if it is whitespace, and the
+     *  preceeding character is not an escape character.
+     */
+    public boolean isDelimiterChar(String buffer, int pos) {
+        return Character.isWhitespace(buffer.charAt(pos));
+    }
+
+    static class ArgumentCommandLine implements CommandLine {
+        private final String argument;
+        private final int position;
+
+        ArgumentCommandLine(String argument, int position) {
+            this.argument = argument;
+            this.position = position;
+        }
+
+        @Override
+        public int getCursorArgumentIndex() {
+            return 0;
+        }
+
+        @Override
+        public String getCursorArgument() {
+            return argument;
+        }
+
+        @Override
+        public int getArgumentPosition() {
+            return position;
+        }
+
+        @Override
+        public String[] getArguments() {
+            return new String[] { argument };
+        }
+
+        @Override
+        public int getBufferPosition() {
+            return position;
+        }
+
+        @Override
+        public String getBuffer() {
+            return argument;
+        }
+    }
+}