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:28 UTC

[06/10] git commit: [KARAF-2805] Add a compatibility layer and switch to the new console

[KARAF-2805] Add a compatibility layer and switch to the new console

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

Branch: refs/heads/master
Commit: 2e3c3ef97965351dcd9e6f7e562d434793f82681
Parents: e7d23be
Author: Guillaume Nodet <gn...@gmail.com>
Authored: Wed Mar 5 15:16:38 2014 +0100
Committer: Guillaume Nodet <gn...@gmail.com>
Committed: Wed Mar 5 15:37:33 2014 +0100

----------------------------------------------------------------------
 assemblies/features/framework/pom.xml           |   4 +-
 .../apache/karaf/itests/KarafTestSupport.java   |  10 +-
 .../apache/karaf/itests/SshCommandTestBase.java |   2 +-
 shell/console/pom.xml                           |  15 +-
 .../karaf/shell/compat/ArgumentCompleter.java   | 479 +++++++++++++++++++
 .../karaf/shell/compat/CommandTracker.java      | 166 +++++++
 .../shell/compat/OldArgumentCompleter.java      | 450 +++++++++++++++++
 .../OSGI-INF/blueprint/karaf-console.xml        |  71 +--
 .../src/main/resources/OSGI-INF/bundle.info     |   4 +-
 9 files changed, 1121 insertions(+), 80 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/2e3c3ef9/assemblies/features/framework/pom.xml
----------------------------------------------------------------------
diff --git a/assemblies/features/framework/pom.xml b/assemblies/features/framework/pom.xml
index 6a26aae..ed48318 100644
--- a/assemblies/features/framework/pom.xml
+++ b/assemblies/features/framework/pom.xml
@@ -159,11 +159,11 @@
 
         <dependency>
             <groupId>org.apache.karaf.shell</groupId>
-            <artifactId>org.apache.karaf.shell.console</artifactId>
+            <artifactId>org.apache.karaf.shell.core</artifactId>
         </dependency>
         <dependency>
             <groupId>org.apache.karaf.shell</groupId>
-            <artifactId>org.apache.karaf.shell.help</artifactId>
+            <artifactId>org.apache.karaf.shell.console</artifactId>
         </dependency>
         <dependency>
             <groupId>org.apache.karaf.shell</groupId>

http://git-wip-us.apache.org/repos/asf/karaf/blob/2e3c3ef9/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java
----------------------------------------------------------------------
diff --git a/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java b/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java
index ccde975..4507d5f 100644
--- a/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java
+++ b/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java
@@ -51,11 +51,11 @@ import javax.management.remote.JMXConnectorFactory;
 import javax.management.remote.JMXServiceURL;
 import javax.security.auth.Subject;
 
-import org.apache.felix.service.command.CommandProcessor;
-import org.apache.felix.service.command.CommandSession;
 import org.apache.karaf.features.BootFinished;
 import org.apache.karaf.features.Feature;
 import org.apache.karaf.features.FeaturesService;
+import org.apache.karaf.shell.api.console.Session;
+import org.apache.karaf.shell.api.console.SessionFactory;
 import org.junit.Assert;
 import org.junit.Rule;
 import org.ops4j.pax.exam.Configuration;
@@ -161,8 +161,8 @@ public class KarafTestSupport {
         String response;
         final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
         final PrintStream printStream = new PrintStream(byteArrayOutputStream);
-        final CommandProcessor commandProcessor = getOsgiService(CommandProcessor.class);
-        final CommandSession commandSession = commandProcessor.createSession(System.in, printStream, System.err);
+        final SessionFactory sessionFactory = getOsgiService(SessionFactory.class);
+        final Session session = sessionFactory.create(System.in, printStream, System.err);
 
         final Callable<String> commandCallable = new Callable<String>() {
             @Override
@@ -171,7 +171,7 @@ public class KarafTestSupport {
                     if (!silent) {
                         System.err.println(command);
                     }
-                    commandSession.execute(command);
+                    session.execute(command);
                 } catch (Exception e) {
                     throw new RuntimeException(e.getMessage(), e);
                 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/2e3c3ef9/itests/src/test/java/org/apache/karaf/itests/SshCommandTestBase.java
----------------------------------------------------------------------
diff --git a/itests/src/test/java/org/apache/karaf/itests/SshCommandTestBase.java b/itests/src/test/java/org/apache/karaf/itests/SshCommandTestBase.java
index adc1cc0..e687f4c 100644
--- a/itests/src/test/java/org/apache/karaf/itests/SshCommandTestBase.java
+++ b/itests/src/test/java/org/apache/karaf/itests/SshCommandTestBase.java
@@ -21,7 +21,7 @@ import java.io.PipedOutputStream;
 import java.util.Arrays;
 import java.util.HashSet;
 
-import junit.framework.Assert;
+import org.junit.Assert;
 
 import org.apache.karaf.features.Feature;
 import org.apache.sshd.ClientChannel;

http://git-wip-us.apache.org/repos/asf/karaf/blob/2e3c3ef9/shell/console/pom.xml
----------------------------------------------------------------------
diff --git a/shell/console/pom.xml b/shell/console/pom.xml
index df03dd9..e44682c 100644
--- a/shell/console/pom.xml
+++ b/shell/console/pom.xml
@@ -31,7 +31,7 @@
     <artifactId>org.apache.karaf.shell.console</artifactId>
     <packaging>bundle</packaging>
     <name>Apache Karaf :: Shell :: Console</name>
-    <description>This bundle provides OSGi shell integration and console support.</description>
+    <description>This bundle provides compatibility with the previous console.</description>
 
     <properties>
         <appendedResourcesDirectory>${basedir}/../../etc/appended-resources</appendedResourcesDirectory>
@@ -65,6 +65,10 @@
             <groupId>org.apache.karaf.jaas</groupId>
             <artifactId>org.apache.karaf.jaas.boot</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.karaf.shell</groupId>
+            <artifactId>org.apache.karaf.shell.core</artifactId>
+        </dependency>
 
         <dependency>
             <groupId>org.apache.aries.blueprint</groupId>
@@ -132,6 +136,9 @@
                 <artifactId>maven-bundle-plugin</artifactId>
                 <configuration>
                     <instructions>
+                        <Fragment-Host>
+                            org.apache.karaf.shell.core
+                        </Fragment-Host>
                         <Import-Package>
                             !org.apache.felix.gogo.runtime.*,
                             !org.apache.karaf.shell.inject.impl,
@@ -165,15 +172,11 @@
                         </Export-Package>
                         <Private-Package>
                         	org.apache.karaf.shell.commands.ansi,
+                            org.apache.karaf.shell.compat,
                         	org.apache.karaf.shell.console.impl*,
                             org.apache.karaf.shell.security.impl*,
                             org.apache.karaf.shell.inject.impl*,
-                            org.apache.felix.utils.extender,
-                            org.apache.felix.utils.manifest
                         </Private-Package>
-                        <Bundle-Activator>
-                            org.apache.felix.gogo.runtime.activator.Activator
-                        </Bundle-Activator>
                         <Main-Class>
                             org.apache.karaf.shell.console.impl.Main
                         </Main-Class>

http://git-wip-us.apache.org/repos/asf/karaf/blob/2e3c3ef9/shell/console/src/main/java/org/apache/karaf/shell/compat/ArgumentCompleter.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/compat/ArgumentCompleter.java b/shell/console/src/main/java/org/apache/karaf/shell/compat/ArgumentCompleter.java
new file mode 100644
index 0000000..8f52503
--- /dev/null
+++ b/shell/console/src/main/java/org/apache/karaf/shell/compat/ArgumentCompleter.java
@@ -0,0 +1,479 @@
+/*
+ * 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.compat;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+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.felix.gogo.commands.Action;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.CommandWithAction;
+import org.apache.karaf.shell.commands.CompleterValues;
+import org.apache.karaf.shell.commands.HelpOption;
+import org.apache.karaf.shell.commands.Option;
+import org.apache.karaf.shell.console.CommandSessionHolder;
+import org.apache.karaf.shell.console.CompletableFunction;
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.completer.FileCompleter;
+import org.apache.karaf.shell.console.completer.NullCompleter;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.apache.karaf.shell.api.console.CommandLine;
+import org.apache.karaf.shell.api.console.Session;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ArgumentCompleter {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ArgumentCompleter.class);
+
+    public static final String ARGUMENTS_LIST = "ARGUMENTS_LIST";
+
+    public static final String COMMANDS = ".commands";
+
+    final String scope;
+    final String name;
+    final Completer commandCompleter;
+    final Completer optionsCompleter;
+    final List<Completer> argsCompleters;
+    final Map<String, Completer> optionalCompleters;
+    final CommandWithAction function;
+    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(CommandWithAction function, String scope, String name, boolean scoped) {
+        this.function = function;
+        this.scope = scope;
+        this.name = name;
+        // Command name completer
+        String[] names = scoped ? new String[] { name } : new String[] { name, scope + ":" + name };
+        commandCompleter = new StringsCompleter(names);
+        // Build options completer
+        for (Class<?> type = function.getActionClass(); 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);
+        optionsCompleter = new StringsCompleter(options.keySet());
+
+        // Build arguments completers
+        List<Completer> argsCompleters = null;
+        Map<String, Completer> optionalCompleters = null;
+
+        if (function instanceof CompletableFunction) {
+            Map<String, Completer> focl = ((CompletableFunction) function).getOptionalCompleters();
+            List<Completer> fcl = ((CompletableFunction) function).getCompleters();
+            if (focl != null || fcl != null) {
+                argsCompleters = new ArrayList<Completer>();
+                if (fcl != null) {
+                    for (Completer c : fcl) {
+                        argsCompleters.add(c == null ? NullCompleter.INSTANCE : c);
+                    }
+                }
+                optionalCompleters = focl;
+            }
+        }
+        if (argsCompleters == null) {
+            final Map<Integer, Object> values = getCompleterValues(function);
+            argsCompleters = new ArrayList<Completer>();
+            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());
+                    org.apache.karaf.shell.commands.Completer ann = field.getAnnotation(org.apache.karaf.shell.commands.Completer.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 {
+                                BundleContext context = FrameworkUtil.getBundle(function.getClass()).getBundleContext();
+                                completer = new ProxyServiceCompleter(context, clazz);
+                            }
+                        }
+                    } else if (values.containsKey(key)) {
+                        Object value = values.get(key);
+                        if (value instanceof String[]) {
+                            completer = new StringsCompleter((String[]) value);
+                        } else if (value instanceof Collection) {
+                            completer = new StringsCompleter((Collection<String>) value);
+                        } else {
+                            LOGGER.warn("Could not use value " + value + " as set of completions!");
+                        }
+                    } 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) {
+                    org.apache.karaf.shell.commands.Completer ann = field.getAnnotation(org.apache.karaf.shell.commands.Completer.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 {
+                                BundleContext context = FrameworkUtil.getBundle(function.getClass()).getBundleContext();
+                                completer = new ProxyServiceCompleter(context, 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);
+                    }
+                }
+            }
+        }
+        this.argsCompleters = argsCompleters;
+        this.optionalCompleters = optionalCompleters;
+    }
+
+    private Map<Integer, Object> getCompleterValues(CommandWithAction function) {
+        final Map<Integer, Object> values = new HashMap<Integer, Object>();
+        Action action = null;
+        try {
+            for (Class<?> type = function.getActionClass(); type != null; type = type.getSuperclass()) {
+                for (Method method : type.getDeclaredMethods()) {
+                    CompleterValues completerMethod = method.getAnnotation(CompleterValues.class);
+                    if (completerMethod != null) {
+                        int index = completerMethod.index();
+                        Integer key = index;
+                        if (index >= arguments.size() || index < 0) {
+                            LOGGER.warn("Index out of range on @CompleterValues on class " + type.getName() + " for index: " + key + " see: " + method);
+                        } else if (values.containsKey(key)) {
+                            LOGGER.warn("Duplicate @CompleterMethod annotations on class " + type.getName() + " for index: " + key + " see: " + method);
+                        } else {
+                            try {
+                                Object value;
+                                if (Modifier.isStatic(method.getModifiers())) {
+                                    value = method.invoke(null);
+                                } else {
+                                    if (action == null) {
+                                        action = function.createNewAction();
+                                    }
+                                    value = method.invoke(action);
+                                }
+                                values.put(key, value);
+                            } catch (IllegalAccessException e) {
+                                LOGGER.warn("Could not invoke @CompleterMethod on " + function + ". " + e, e);
+                            } catch (InvocationTargetException e) {
+                                Throwable target = e.getTargetException();
+                                if (target == null) {
+                                    target = e;
+                                }
+                                LOGGER.warn("Could not invoke @CompleterMethod on " + function + ". " + target, target);
+                            }
+                        }
+                    }
+                }
+            }
+        } finally {
+            if (action != null) {
+                try {
+                    function.releaseAction(action);
+                } catch (Exception e) {
+                    LOGGER.warn("Failed to release action: " + action + ". " + e, e);
+                }
+            }
+        }
+        return values;
+    }
+
+    private Completer getDefaultCompleter(Field field) {
+        Completer completer = null;
+        Class<?> type = field.getType();
+        if (type.isAssignableFrom(File.class)) {
+            completer = new FileCompleter(null);
+        } 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(final Session session, final CommandLine list, final List<String> candidates) {
+        int argpos = list.getArgumentPosition();
+        int argIndex = list.getCursorArgumentIndex();
+
+        //Store the argument list so that it can be used by completers.
+        CommandSession commandSession = CommandSessionHolder.getSession();
+        if(commandSession != null) {
+            commandSession.put(ARGUMENTS_LIST,list);
+        }
+
+        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(scope) && !session.resolveCommand(args[index]).equals(scope + ":" + name)) {
+                return -1;
+            }
+            // Verify command name
+            if (!verifyCompleter(commandCompleter, args[index])) {
+                return -1;
+            }
+            index++;
+        } else {
+            comp = commandCompleter;
+        }
+        // Now, check options
+        if (comp == null) {
+            while (index < argIndex && args[index].startsWith("-")) {
+                if (!verifyCompleter(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 (optionalCompleters != null && 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(sub, args[index])) {
+                    return -1;
+                }
+                index++;
+                indexArg++;
+            }
+            comp = argsCompleters.get(indexArg >= argsCompleters.size() ? argsCompleters.size() - 1 : indexArg);
+        }
+
+        int ret = comp.complete(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(Completer completer, String argument) {
+        List<String> candidates = new ArrayList<String>();
+        return completer.complete(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));
+    }
+
+    public static class ProxyServiceCompleter implements Completer {
+        private final BundleContext context;
+        private final Class<? extends Completer> clazz;
+
+        public ProxyServiceCompleter(BundleContext context, Class<? extends Completer> clazz) {
+            this.context = context;
+            this.clazz = clazz;
+        }
+
+        @Override
+        public int complete(String buffer, int cursor, List<String> candidates) {
+            ServiceReference<? extends Completer> ref = context.getServiceReference(clazz);
+            if (ref != null) {
+                Completer completer = context.getService(ref);
+                if (completer != null) {
+                    try {
+                        return completer.complete(buffer, cursor, candidates);
+                    } finally {
+                        context.ungetService(ref);
+                    }
+                }
+            }
+            return -1;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2e3c3ef9/shell/console/src/main/java/org/apache/karaf/shell/compat/CommandTracker.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/compat/CommandTracker.java b/shell/console/src/main/java/org/apache/karaf/shell/compat/CommandTracker.java
new file mode 100644
index 0000000..191735c
--- /dev/null
+++ b/shell/console/src/main/java/org/apache/karaf/shell/compat/CommandTracker.java
@@ -0,0 +1,166 @@
+/*
+ * 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.compat;
+
+import java.util.List;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.CommandWithAction;
+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.api.console.SessionFactory;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+public class CommandTracker implements ServiceTrackerCustomizer<Object, Object> {
+
+    SessionFactory sessionFactory;
+    BundleContext context;
+    ServiceTracker<?, ?> tracker;
+
+    public void setSessionFactory(SessionFactory sessionFactory) {
+        this.sessionFactory = sessionFactory;
+    }
+
+    public void setContext(BundleContext context) {
+        this.context = context;
+    }
+
+    public void init() throws Exception {
+        Filter filter = context.createFilter(String.format("(&(%s=*)(%s=*))",
+                CommandProcessor.COMMAND_SCOPE, CommandProcessor.COMMAND_FUNCTION));
+        this.tracker = new ServiceTracker<Object, Object>(context, filter, this);
+        this.tracker.open();
+    }
+
+    public void destroy() {
+        tracker.close();
+    }
+
+    @Override
+    public Object addingService(final ServiceReference reference) {
+        Object service = context.getService(reference);
+        if (service instanceof CommandWithAction) {
+            final CommandWithAction oldCommand = (CommandWithAction) service;
+            final org.apache.karaf.shell.api.console.Command command = new org.apache.karaf.shell.api.console.Command() {
+                @Override
+                public String getScope() {
+                    return reference.getProperty(CommandProcessor.COMMAND_SCOPE).toString();
+                }
+
+                @Override
+                public String getName() {
+                    return reference.getProperty(CommandProcessor.COMMAND_FUNCTION).toString();
+                }
+
+                @Override
+                public String getDescription() {
+                    final Command cmd = oldCommand.getActionClass().getAnnotation(Command.class);
+                    if (cmd != null) {
+                        return cmd.description();
+                    } else {
+                        return getName();
+                    }
+                }
+
+                @Override
+                public Completer getCompleter(final boolean scoped) {
+                    final ArgumentCompleter completer = new ArgumentCompleter(oldCommand, getScope(), getName(), scoped);
+                    return new Completer() {
+                        @Override
+                        public int complete(Session session, CommandLine commandLine, List<String> candidates) {
+                            return completer.complete(session, commandLine, candidates);
+                        }
+                    };
+                }
+
+                @Override
+                public Object execute(Session session, List<Object> arguments) throws Exception {
+                    // TODO: remove not really nice cast
+                    CommandSession commandSession = (CommandSession) session.get(".commandSession");
+                    return oldCommand.execute(commandSession, arguments);
+                }
+            };
+            sessionFactory.getRegistry().register(command);
+            return command;
+        } else if (service instanceof org.apache.felix.gogo.commands.CommandWithAction) {
+            final org.apache.felix.gogo.commands.CommandWithAction oldCommand = (org.apache.felix.gogo.commands.CommandWithAction) service;
+            final org.apache.karaf.shell.api.console.Command command = new org.apache.karaf.shell.api.console.Command() {
+                @Override
+                public String getScope() {
+                    return reference.getProperty(CommandProcessor.COMMAND_SCOPE).toString();
+                }
+
+                @Override
+                public String getName() {
+                    return reference.getProperty(CommandProcessor.COMMAND_FUNCTION).toString();
+                }
+
+                @Override
+                public String getDescription() {
+                    final org.apache.felix.gogo.commands.Command cmd = oldCommand.getActionClass().getAnnotation(org.apache.felix.gogo.commands.Command.class);
+                    if (cmd != null) {
+                        return cmd.description();
+                    } else {
+                        return getName();
+                    }
+                }
+
+                @Override
+                public Completer getCompleter(final boolean scoped) {
+                    final OldArgumentCompleter completer = new OldArgumentCompleter(oldCommand, getScope(), getName(), scoped);
+                    return new Completer() {
+                        @Override
+                        public int complete(Session session, CommandLine commandLine, List<String> candidates) {
+                            return completer.complete(session, commandLine, candidates);
+                        }
+                    };
+                }
+
+                @Override
+                public Object execute(Session session, List<Object> arguments) throws Exception {
+                    // TODO: remove not really nice cast
+                    CommandSession commandSession = (CommandSession) session.get(".commandSession");
+                    return oldCommand.execute(commandSession, arguments);
+                }
+            };
+            sessionFactory.getRegistry().register(command);
+            return command;
+        }
+        return service;
+    }
+
+    @Override
+    public void modifiedService(ServiceReference reference, Object service) {
+    }
+
+    @Override
+    public void removedService(ServiceReference reference, Object service) {
+        if (service instanceof org.apache.karaf.shell.api.console.Command) {
+            sessionFactory.getRegistry().unregister(service);
+        }
+        context.ungetService(reference);
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2e3c3ef9/shell/console/src/main/java/org/apache/karaf/shell/compat/OldArgumentCompleter.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/compat/OldArgumentCompleter.java b/shell/console/src/main/java/org/apache/karaf/shell/compat/OldArgumentCompleter.java
new file mode 100644
index 0000000..407244c
--- /dev/null
+++ b/shell/console/src/main/java/org/apache/karaf/shell/compat/OldArgumentCompleter.java
@@ -0,0 +1,450 @@
+/*
+ * 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.compat;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+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.felix.gogo.commands.Action;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.felix.gogo.commands.CommandWithAction;
+import org.apache.felix.gogo.commands.CompleterValues;
+import org.apache.felix.gogo.commands.Option;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.api.console.CommandLine;
+import org.apache.karaf.shell.api.console.Session;
+import org.apache.karaf.shell.console.CommandSessionHolder;
+import org.apache.karaf.shell.console.CompletableFunction;
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.NameScoping;
+import org.apache.karaf.shell.console.completer.FileCompleter;
+import org.apache.karaf.shell.console.completer.NullCompleter;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OldArgumentCompleter {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(OldArgumentCompleter.class);
+
+    public static final String ARGUMENTS_LIST = "ARGUMENTS_LIST";
+
+    final String scope;
+    final String name;
+    final Completer commandCompleter;
+    final Completer optionsCompleter;
+    final List<Completer> argsCompleters;
+    final Map<String, Completer> optionalCompleters;
+    final CommandWithAction function;
+    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 OldArgumentCompleter(CommandWithAction function, String scope, String name, boolean scoped) {
+        this.function = function;
+        this.scope = scope;
+        this.name = name;
+        // Command name completer
+        String[] names = scoped ? new String[] { name } : new String[] { name, scope + ":" + name };
+        commandCompleter = new StringsCompleter(names);
+        // Build options completer
+        for (Class<?> type = function.getActionClass(); 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);
+        optionsCompleter = new StringsCompleter(options.keySet());
+        // Build arguments completers
+        argsCompleters = new ArrayList<Completer>();
+
+        if (function instanceof CompletableFunction) {
+            Map<String, Completer> opt;
+            try {
+                //
+                opt = ((CompletableFunction) function).getOptionalCompleters();
+            } catch (Throwable t) {
+                opt = new HashMap<String, Completer>();
+            }
+            optionalCompleters = opt;
+            List<Completer> fcl = ((CompletableFunction) function).getCompleters();
+            if (fcl != null) {
+                for (Completer c : fcl) {
+                    argsCompleters.add(c == null ? NullCompleter.INSTANCE : c);
+                }
+            } else {
+                argsCompleters.add(NullCompleter.INSTANCE);
+            }
+        } else {
+            optionalCompleters = new HashMap<String, Completer>();
+            final Map<Integer, Method> methods = new HashMap<Integer, Method>();
+            for (Class<?> type = function.getActionClass(); type != null; type = type.getSuperclass()) {
+                for (Method method : type.getDeclaredMethods()) {
+                    CompleterValues completerMethod = method.getAnnotation(CompleterValues.class);
+                    if (completerMethod != null) {
+                        int index = completerMethod.index();
+                        Integer key = index;
+                        if (index >= arguments.size() || index < 0) {
+                            LOGGER.warn("Index out of range on @CompleterValues on class " + type.getName() + " for index: " + key + " see: " + method);
+                        }
+                        if (methods.containsKey(key)) {
+                            LOGGER.warn("Duplicate @CompleterMethod annotations on class " + type.getName() + " for index: " + key + " see: " + method);
+                        } else {
+                            methods.put(key, method);
+                        }
+                    }
+                }
+            }
+            for (int i = 0, size = arguments.size(); i < size; i++) {
+                Completer argCompleter = NullCompleter.INSTANCE;
+                Method method = methods.get(i);
+                if (method != null) {
+                    // lets invoke the method
+                    Action action = function.createNewAction();
+                    try {
+                        Object value = method.invoke(action);
+                        if (value instanceof String[]) {
+                            argCompleter = new StringsCompleter((String[]) value);
+                        } else if (value instanceof Collection) {
+                            argCompleter = new StringsCompleter((Collection<String>) value);
+                        } else {
+                            LOGGER.warn("Could not use value " + value + " as set of completions!");
+                        }
+                    } catch (IllegalAccessException e) {
+                        LOGGER.warn("Could not invoke @CompleterMethod on " + function + ". " + e, e);
+                    } catch (InvocationTargetException e) {
+                        Throwable target = e.getTargetException();
+                        if (target == null) {
+                            target = e;
+                        }
+                        LOGGER.warn("Could not invoke @CompleterMethod on " + function + ". " + target, target);
+                    } finally {
+                        try {
+                            function.releaseAction(action);
+                        } catch (Exception e) {
+                            LOGGER.warn("Failed to release action: " + action + ". " + e, e);
+                        }
+                    }
+                } else {
+                    Field field = arguments.get(i);
+                    Class<?> type = field.getType();
+                    if (type.isAssignableFrom(File.class)) {
+                        argCompleter = new FileCompleter(null);
+                    } else if (type.isAssignableFrom(Boolean.class) || type.isAssignableFrom(boolean.class)) {
+                        argCompleter = 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());
+                        }
+                        argCompleter = new StringsCompleter(values, false);
+                    } else {
+                        // TODO any other completers we can add?
+                    }
+                }
+                argsCompleters.add(argCompleter);
+            }
+        }
+    }
+
+    private String[] getNames(CommandSession session, String scopedCommand) {
+        String command = NameScoping.getCommandNameWithoutGlobalPrefix(session, scopedCommand);
+        String[] s = command.split(":");
+        if (s.length == 1) {
+            return s;
+        } else {
+            return new String[] { command, s[1] };
+        }
+    }
+
+    /**
+     *  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(final Session session, final CommandLine list, final List<String> candidates) {
+        int argpos = list.getArgumentPosition();
+        int argIndex = list.getCursorArgumentIndex();
+
+        //Store the argument list so that it can be used by completers.
+        CommandSession commandSession = CommandSessionHolder.getSession();
+        if(commandSession != null) {
+            commandSession.put(ARGUMENTS_LIST,list);
+        }
+
+        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(scope) && !session.resolveCommand(args[index]).equals(scope + ":" + name)) {
+                return -1;
+            }
+            // Verify command name
+            if (!verifyCompleter(commandCompleter, args[index])) {
+                return -1;
+            }
+            index++;
+        } else {
+            comp = commandCompleter;
+        }
+        // Now, check options
+        if (comp == null) {
+            while (index < argIndex && args[index].startsWith("-")) {
+                if (!verifyCompleter(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 (optionalCompleters != null && 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(sub, args[index])) {
+                    return -1;
+                }
+                index++;
+                indexArg++;
+            }
+            comp = argsCompleters.get(indexArg >= argsCompleters.size() ? argsCompleters.size() - 1 : indexArg);
+        }
+
+        int ret = comp.complete(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(Completer completer, String argument) {
+        List<String> candidates = new ArrayList<String>();
+        return completer.complete(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));
+    }
+
+    /**
+     *  The result of a delimited buffer.
+     */
+    public static class ArgumentList {
+        private String[] arguments;
+        private int cursorArgumentIndex;
+        private int argumentPosition;
+        private int bufferPosition;
+
+        /**
+         *  @param  arguments           the array of tokens
+         *  @param  cursorArgumentIndex the token index of the cursor
+         *  @param  argumentPosition    the position of the cursor in the
+         *                              current token
+         *  @param  bufferPosition      the position of the cursor in
+         *                              the whole buffer
+         */
+        public ArgumentList(String[] arguments, int cursorArgumentIndex,
+            int argumentPosition, int bufferPosition) {
+            this.arguments = arguments;
+            this.cursorArgumentIndex = cursorArgumentIndex;
+            this.argumentPosition = argumentPosition;
+            this.bufferPosition = bufferPosition;
+        }
+
+        public void setCursorArgumentIndex(int cursorArgumentIndex) {
+            this.cursorArgumentIndex = cursorArgumentIndex;
+        }
+
+        public int getCursorArgumentIndex() {
+            return this.cursorArgumentIndex;
+        }
+
+        public String getCursorArgument() {
+            if ((cursorArgumentIndex < 0)
+                || (cursorArgumentIndex >= arguments.length)) {
+                return null;
+            }
+
+            return arguments[cursorArgumentIndex];
+        }
+
+        public void setArgumentPosition(int argumentPosition) {
+            this.argumentPosition = argumentPosition;
+        }
+
+        public int getArgumentPosition() {
+            return this.argumentPosition;
+        }
+
+        public void setArguments(String[] arguments) {
+            this.arguments = arguments;
+        }
+
+        public String[] getArguments() {
+            return this.arguments;
+        }
+
+        public void setBufferPosition(int bufferPosition) {
+            this.bufferPosition = bufferPosition;
+        }
+
+        public int getBufferPosition() {
+            return this.bufferPosition;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/2e3c3ef9/shell/console/src/main/resources/OSGI-INF/blueprint/karaf-console.xml
----------------------------------------------------------------------
diff --git a/shell/console/src/main/resources/OSGI-INF/blueprint/karaf-console.xml b/shell/console/src/main/resources/OSGI-INF/blueprint/karaf-console.xml
index f8b8837..6c41a6e 100644
--- a/shell/console/src/main/resources/OSGI-INF/blueprint/karaf-console.xml
+++ b/shell/console/src/main/resources/OSGI-INF/blueprint/karaf-console.xml
@@ -17,69 +17,14 @@
     limitations under the License.
 
 -->
-<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
-           xmlns:ext="http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.0.0">
-
-    <ext:property-placeholder placeholder-prefix="$[" placeholder-suffix="]">
-        <ext:default-properties>
-            <ext:property name="karaf.startLocalConsole" value="true"/>
-        </ext:default-properties>
-    </ext:property-placeholder>
-
-    <reference id="commandProcessor" interface="org.apache.felix.service.command.CommandProcessor"/>
-    <reference id="threadIO" interface="org.apache.felix.service.threadio.ThreadIO" />
-
-    <bean id="consoleFactoryService" class="org.apache.karaf.shell.console.impl.jline.ConsoleFactoryService">
-        <argument ref="blueprintBundleContext"/>
-        <argument ref="commandProcessor"/>
-        <argument ref="threadIO"/>
-    </bean>
-    <service interface="org.apache.karaf.shell.console.factory.ConsoleFactory" ref="consoleFactoryService"/>
-
-    <bean id="consoleFactory" class="org.apache.karaf.shell.console.impl.jline.LocalConsoleManager"
-          destroy-method="stop">
-        <argument value="$[karaf.startLocalConsole]"/>
-        <argument value="$[org.osgi.framework.startlevel.beginning]"/>
-        <argument ref="blueprintBundleContext"/>
-        <argument ref="terminalFactory"/>
-        <argument ref="consoleFactoryService"/>
-    </bean>
-
-    <bean id="converters" class="org.apache.karaf.shell.console.impl.Converters">
-        <argument ref="blueprintBundleContext"/>
-    </bean>
-    <service ref="converters" interface="org.apache.felix.service.command.Converter"/>
-
-    <bean id="terminalFactory" class="org.apache.karaf.shell.console.impl.jline.TerminalFactory"
-          destroy-method="destroy"/>
-
-    <service>
-        <interfaces>
-            <value>org.apache.felix.service.command.Function</value>
-            <value>org.apache.karaf.shell.console.CompletableFunction</value>
-        </interfaces>
-        <service-properties>
-            <entry key="osgi.command.scope" value="*"/>
-            <entry key="osgi.command.function" value="exit"/>
-        </service-properties>
-        <bean class="org.apache.karaf.shell.console.commands.BlueprintCommand">
-            <property name="blueprintContainer" ref="blueprintContainer"/>
-            <property name="blueprintConverter" ref="blueprintConverter"/>
-            <property name="actionId" value="exit"/>
-        </bean>
-    </service>
-
-    <bean id="exit" class="org.apache.karaf.shell.console.ExitAction" activation="lazy" scope="prototype"/>
-
-    <!-- Get a reference to the Configuration Admin Service -->
-    <reference id="configAdmin" interface="org.osgi.service.cm.ConfigurationAdmin"/>
-
-    <!-- For role-based security on the shell commands -->
-    <bean id="secureCommandConfigTransformer"
-          class="org.apache.karaf.shell.security.impl.SecuredCommandConfigTransformer"
-          init-method="init">
-        <property name="configAdmin" ref="configAdmin"/>
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+    <bean id="commandTracker" class="org.apache.karaf.shell.compat.CommandTracker"
+            init-method="init" destroy-method="destroy">
+        <property name="context" ref="blueprintBundleContext" />
+        <property name="sessionFactory">
+            <reference interface="org.apache.karaf.shell.api.console.SessionFactory" />
+        </property>
     </bean>
-    <service ref="secureCommandConfigTransformer" interface="org.osgi.service.cm.ConfigurationListener"/>
 
 </blueprint>

http://git-wip-us.apache.org/repos/asf/karaf/blob/2e3c3ef9/shell/console/src/main/resources/OSGI-INF/bundle.info
----------------------------------------------------------------------
diff --git a/shell/console/src/main/resources/OSGI-INF/bundle.info b/shell/console/src/main/resources/OSGI-INF/bundle.info
index 4d4296e..23fcee4 100644
--- a/shell/console/src/main/resources/OSGI-INF/bundle.info
+++ b/shell/console/src/main/resources/OSGI-INF/bundle.info
@@ -9,8 +9,6 @@ Maven URL:
 
 h1. Description
 
-This bundle provides the integration of Apache Felix Gogo, shell, and console.
-
-It provides the default Karaf branding including the default welcome message.
+This bundle provides the compatibility layer with Karaf 2.x and 3.x console.
 
 h1. See also