You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by gn...@apache.org on 2009/07/09 10:29:22 UTC

svn commit: r792445 [3/6] - in /felix/trunk/karaf: ./ assembly/ assembly/src/main/descriptors/ assembly/src/main/filtered-resources/ assembly/src/main/filtered-resources/etc/ client/ deployer/features/src/main/resources/OSGI-INF/blueprint/ deployer/fil...

Copied: felix/trunk/karaf/gshell/gshell-console/pom.xml (from r792359, felix/trunk/karaf/gshell/gshell-wrapper/pom.xml)
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/gshell/gshell-console/pom.xml?p2=felix/trunk/karaf/gshell/gshell-console/pom.xml&p1=felix/trunk/karaf/gshell/gshell-wrapper/pom.xml&r1=792359&r2=792445&rev=792445&view=diff
==============================================================================
--- felix/trunk/karaf/gshell/gshell-wrapper/pom.xml (original)
+++ felix/trunk/karaf/gshell/gshell-console/pom.xml Thu Jul  9 08:29:14 2009
@@ -28,42 +28,41 @@
     </parent>
 
     <groupId>org.apache.felix.karaf.gshell</groupId>
-    <artifactId>org.apache.felix.karaf.gshell.wrapper</artifactId>
+    <artifactId>org.apache.felix.karaf.gshell.console</artifactId>
     <packaging>bundle</packaging>
     <version>1.2.0-SNAPSHOT</version>
-    <name>Apache Felix Karaf :: GShell Service Wrapper</name>
+    <name>Apache Felix Karaf :: GShell Console</name>
 
     <description>
-        Provides the Service Wrapper GShell integration
+        Provides the OSGi GShell integration
     </description>
 
-    <properties>
-        <gshell.osgi.import>
-            org.apache.felix.karaf.main.spi.*;resolution:=optional,
-            org.apache.geronimo.gshell*,
-        </gshell.osgi.import>
-        <gshell.osgi.export>
-        </gshell.osgi.export>
-        <gshell.osgi.private>
-            org.apache.felix.karaf.gshell.wrapper.*,
-        </gshell.osgi.private>
-    </properties>
-
     <dependencies>
         <dependency>
-            <groupId>org.apache.felix.karaf.gshell</groupId>
-            <artifactId>org.apache.felix.karaf.gshell.core</artifactId>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.jline</artifactId>
         </dependency>
-
         <dependency>
             <groupId>org.apache.felix</groupId>
             <artifactId>org.osgi.core</artifactId>
             <scope>provided</scope>
         </dependency>
-
         <dependency>
-            <groupId>org.springframework.osgi</groupId>
-            <artifactId>spring-osgi-core</artifactId>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.geronimo</groupId>
+            <artifactId>blueprint-bundle</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix.gogo</groupId>
+            <artifactId>org.apache.felix.gogo.runtime</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix.gogo</groupId>
+            <artifactId>org.apache.felix.gogo.commands</artifactId>
         </dependency>
     </dependencies>
 
@@ -97,17 +96,16 @@
                 <configuration>
                     <instructions>
                         <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
-                        <Export-Package>${pom.artifactId}*;version=${project.version}</Export-Package>
                         <Import-Package>
-                            org.apache.geronimo.gshell.command,
-                            org.apache.geronimo.gshell.wisdom.command,
-                            org.apache.geronimo.gshell.wisdom.registry,
-                            org.apache.felix.karaf.gshell.core,
+                            !javax.swing,
                             *
                         </Import-Package>
-                        <Private-Package>!*</Private-Package>
+                        <Export-Package>
+                            org.apache.felix.karaf.gshell.console*;version=${pom.version}
+                        </Export-Package>
                         <_versionpolicy>${bnd.version.policy}</_versionpolicy>
                     </instructions>
+                    <unpackBundle>true</unpackBundle>
                 </configuration>
             </plugin>
         </plugins>

Copied: felix/trunk/karaf/gshell/gshell-console/src/main/filtered-resources/org/apache/felix/karaf/gshell/console/branding.properties (from r792359, felix/trunk/karaf/gshell/gshell-core/src/main/resources/org/apache/geronimo/gshell/commands/ssh/SshAction.properties)
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/gshell/gshell-console/src/main/filtered-resources/org/apache/felix/karaf/gshell/console/branding.properties?p2=felix/trunk/karaf/gshell/gshell-console/src/main/filtered-resources/org/apache/felix/karaf/gshell/console/branding.properties&p1=felix/trunk/karaf/gshell/gshell-core/src/main/resources/org/apache/geronimo/gshell/commands/ssh/SshAction.properties&r1=792359&r2=792445&rev=792445&view=diff
==============================================================================
--- felix/trunk/karaf/gshell/gshell-core/src/main/resources/org/apache/geronimo/gshell/commands/ssh/SshAction.properties (original)
+++ felix/trunk/karaf/gshell/gshell-console/src/main/filtered-resources/org/apache/felix/karaf/gshell/console/branding.properties Thu Jul  9 08:29:14 2009
@@ -17,11 +17,16 @@
 ## under the License.
 ##
 
-##
-## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
-##
+welcome = \
+@|cyan        __ __                  ____  |\n\
+@|cyan       / //_/____ __________ _/ __/  |\n\
+@|cyan      / ,<  / __ `/ ___/ __ `/ /_    |\n\
+@|cyan     / /\\| \\|/ /_/ / /  / /_/ / __/    |\n\
+@|cyan    /_/ \\|_\\|\\__,_/_/   \\__,_/_/     |\n\
+\n\
+ @|bold Apache Felix Karaf| (${pom.version})\n\
+\n\
+Type '@|bold help|' for more information.\n
+
 
-command.description=SSH client.
 
-command.manual=\
-  TODO: about manual

Copied: felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/BlueprintContainerAware.java (from r792359, felix/trunk/karaf/gshell/gshell-core/src/main/java/org/apache/felix/karaf/gshell/core/sshd/BogusPasswordAuthenticator.java)
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/BlueprintContainerAware.java?p2=felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/BlueprintContainerAware.java&p1=felix/trunk/karaf/gshell/gshell-core/src/main/java/org/apache/felix/karaf/gshell/core/sshd/BogusPasswordAuthenticator.java&r1=792359&r2=792445&rev=792445&view=diff
==============================================================================
--- felix/trunk/karaf/gshell/gshell-core/src/main/java/org/apache/felix/karaf/gshell/core/sshd/BogusPasswordAuthenticator.java (original)
+++ felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/BlueprintContainerAware.java Thu Jul  9 08:29:14 2009
@@ -16,14 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.felix.karaf.gshell.core.sshd;
+package org.apache.felix.karaf.gshell.console;
 
-import org.apache.sshd.server.PasswordAuthenticator;
+import org.osgi.service.blueprint.container.BlueprintContainer;
 
-public class BogusPasswordAuthenticator implements PasswordAuthenticator {
+public interface BlueprintContainerAware {
 
-    public Object authenticate(String username, String password) {
-        return (username != null && username.equals(password)) ? username : null;
-    }
+    void setBlueprintContainer(BlueprintContainer blueprintContainer);
 
 }

Copied: felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/BundleContextAware.java (from r792359, felix/trunk/karaf/gshell/gshell-core/src/main/java/org/apache/felix/karaf/gshell/core/vfs/mvn/MvnFileSystem.java)
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/BundleContextAware.java?p2=felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/BundleContextAware.java&p1=felix/trunk/karaf/gshell/gshell-core/src/main/java/org/apache/felix/karaf/gshell/core/vfs/mvn/MvnFileSystem.java&r1=792359&r2=792445&rev=792445&view=diff
==============================================================================
--- felix/trunk/karaf/gshell/gshell-core/src/main/java/org/apache/felix/karaf/gshell/core/vfs/mvn/MvnFileSystem.java (original)
+++ felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/BundleContextAware.java Thu Jul  9 08:29:14 2009
@@ -16,20 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.felix.karaf.gshell.core.vfs.mvn;
+package org.apache.felix.karaf.gshell.console;
 
-import org.apache.commons.vfs.FileName;
-import org.apache.commons.vfs.FileObject;
-import org.apache.commons.vfs.FileSystemOptions;
-import org.apache.commons.vfs.provider.url.UrlFileSystem;
+import org.osgi.framework.BundleContext;
 
-public class MvnFileSystem extends UrlFileSystem {
+public interface BundleContextAware {
 
-    protected MvnFileSystem(FileName fileName, FileSystemOptions fileSystemOptions) {
-        super(fileName, fileSystemOptions);
-    }
+    void setBundleContext(BundleContext bundleContext);
 
-    protected FileObject createFile(FileName fileName) {
-        return new MvnFileObject(this, fileName);
-    }
 }

Added: felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/CompletableFunction.java
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/CompletableFunction.java?rev=792445&view=auto
==============================================================================
--- felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/CompletableFunction.java (added)
+++ felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/CompletableFunction.java Thu Jul  9 08:29:14 2009
@@ -0,0 +1,11 @@
+package org.apache.felix.karaf.gshell.console;
+
+import java.util.List;
+
+import org.osgi.service.command.Function;
+
+public interface CompletableFunction extends Function {
+
+    List<Completer> getCompleters();
+
+}

Copied: felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/Completer.java (from r792359, felix/trunk/karaf/gshell/gshell-core/src/main/java/org/apache/felix/karaf/gshell/core/sshd/BogusPasswordAuthenticator.java)
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/Completer.java?p2=felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/Completer.java&p1=felix/trunk/karaf/gshell/gshell-core/src/main/java/org/apache/felix/karaf/gshell/core/sshd/BogusPasswordAuthenticator.java&r1=792359&r2=792445&rev=792445&view=diff
==============================================================================
--- felix/trunk/karaf/gshell/gshell-core/src/main/java/org/apache/felix/karaf/gshell/core/sshd/BogusPasswordAuthenticator.java (original)
+++ felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/Completer.java Thu Jul  9 08:29:14 2009
@@ -7,7 +7,7 @@
  * "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
+ *   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
@@ -16,14 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.felix.karaf.gshell.core.sshd;
+package org.apache.felix.karaf.gshell.console;
 
-import org.apache.sshd.server.PasswordAuthenticator;
+import java.util.List;
 
-public class BogusPasswordAuthenticator implements PasswordAuthenticator {
+public interface Completer {
 
-    public Object authenticate(String username, String password) {
-        return (username != null && username.equals(password)) ? username : null;
-    }
+    int complete(String buffer, int cursor, List<String> candidates);
 
 }

Copied: felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/OsgiCommandSupport.java (from r792359, felix/trunk/karaf/gshell/gshell-core/src/main/java/org/apache/felix/karaf/gshell/core/OsgiCommandSupport.java)
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/OsgiCommandSupport.java?p2=felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/OsgiCommandSupport.java&p1=felix/trunk/karaf/gshell/gshell-core/src/main/java/org/apache/felix/karaf/gshell/core/OsgiCommandSupport.java&r1=792359&r2=792445&rev=792445&view=diff
==============================================================================
--- felix/trunk/karaf/gshell/gshell-core/src/main/java/org/apache/felix/karaf/gshell/core/OsgiCommandSupport.java (original)
+++ felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/OsgiCommandSupport.java Thu Jul  9 08:29:14 2009
@@ -16,33 +16,27 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.felix.karaf.gshell.core;
+package org.apache.felix.karaf.gshell.console;
 
 import java.util.ArrayList;
 import java.util.List;
 
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.geronimo.gshell.command.CommandAction;
-import org.apache.geronimo.gshell.command.CommandContext;
-import org.apache.geronimo.gshell.command.Variables;
-import org.apache.geronimo.gshell.io.IO;
+import org.apache.felix.gogo.commands.Action;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
+import org.osgi.service.command.CommandSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-public abstract class OsgiCommandSupport implements CommandAction {
+public abstract class OsgiCommandSupport implements Action, BundleContextAware {
 
-    protected Log log = LogFactory.getLog(getClass());
+    protected final Logger log = LoggerFactory.getLogger(getClass());
     protected BundleContext bundleContext;
-    protected CommandContext commandContext;
-    protected IO io;
-    protected Variables variables;
+    protected CommandSession session;
     protected List<ServiceReference> usedReferences;
-    
-    public Object execute(CommandContext commandContext) throws Exception {
-        this.commandContext = commandContext;
-        this.io = commandContext.getIo();
-        this.variables = commandContext.getVariables();
+
+    public Object execute(CommandSession session) throws Exception {
+        this.session = session;
         try {
             return doExecute();
         } finally {

Added: felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/ansi/AnsiCode.java
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/ansi/AnsiCode.java?rev=792445&view=auto
==============================================================================
--- felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/ansi/AnsiCode.java (added)
+++ felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/ansi/AnsiCode.java Thu Jul  9 08:29:14 2009
@@ -0,0 +1,49 @@
+package org.apache.felix.karaf.gshell.console.ansi;
+
+public enum AnsiCode
+{
+    OFF(0),
+    BOLD(1),
+    UNDERSCORE(4),
+    BLINK(5),
+    REVERSE(7),
+    CONCEALED(8),
+
+    FG_BLACK(30),
+    FG_RED(31),
+    FG_GREEN(32),
+    FG_YELLOW(33),
+    FG_BLUE(34),
+    FG_MAGENTA(35),
+    FG_CYAN(36),
+    FG_WHITE(37),
+
+    BLACK(FG_BLACK),
+    RED(FG_RED),
+    GREEN(FG_GREEN),
+    YELLOW(FG_YELLOW),
+    BLUE(FG_BLUE),
+    MAGENTA(FG_MAGENTA),
+    CYAN(FG_CYAN),
+    WHITE(FG_WHITE),
+
+    BG_BLACK(40),
+    BG_RED(41),
+    BG_GREEN(42),
+    BG_YELLOW(43),
+    BG_BLUE(44),
+    BG_MAGENTA(45),
+    BG_CYAN(46),
+    BG_WHITE(47);
+
+    final int code;
+
+    private AnsiCode(final int code) {
+        this.code = code;
+    }
+
+    private AnsiCode(final AnsiCode code) {
+        this(code.code);
+    }
+
+}

Added: felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/ansi/AnsiOutputStream.java
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/ansi/AnsiOutputStream.java?rev=792445&view=auto
==============================================================================
--- felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/ansi/AnsiOutputStream.java (added)
+++ felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/ansi/AnsiOutputStream.java Thu Jul  9 08:29:14 2009
@@ -0,0 +1,87 @@
+package org.apache.felix.karaf.gshell.console.ansi;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.ByteArrayOutputStream;
+
+public class AnsiOutputStream extends FilterOutputStream
+{
+    char[] buf = new char[16];
+    int count = 0;
+    int ansiCodeState;
+
+    public AnsiOutputStream(OutputStream out)
+    {
+        super(out);
+    }
+
+    @Override
+    public void write(int b) throws IOException
+    {
+        if (ansiCodeState == 0) {
+            if (b == '@') {
+                ansiCodeState = 1;
+            } else {
+                super.write(b);
+            }
+        } else if (ansiCodeState == 1) {
+            if (b == '|') {
+                ansiCodeState = 2;
+                count = 0;
+            } else {
+                super.write('@');
+                super.write(b);
+                ansiCodeState = 0;
+            }
+        } else if (ansiCodeState == 2) {
+            if (b == ',') {
+                write(AnsiCode.valueOf(new String(buf, 0, count).toUpperCase()));
+                count = 0;
+            } else if (b == ' ') {
+                write(AnsiCode.valueOf(new String(buf, 0, count).toUpperCase()));
+                ansiCodeState = 3;
+            } else if (count < buf.length) {
+                buf[count++] = (char) b;
+            } else {
+                throw new IOException("Unknown ANSI code (too long): " + new String(buf, 0, count));
+            }
+        } else if (ansiCodeState == 3) {
+            if (b == '|') {
+                write(AnsiCode.OFF);
+                ansiCodeState = 0;
+            } else if (b == '\\') {
+                ansiCodeState = 4;
+            } else {
+                super.write(b);
+            }
+        } else if (ansiCodeState == 4) {
+            if (b != '|') {
+                super.write('\\');
+            }
+            super.write(b);
+            ansiCodeState = 3;
+        } else {
+            throw new IllegalStateException();
+        }
+    }
+
+    protected void write(AnsiCode code) throws IOException {
+        super.write(27); // ESC
+        super.write('[');
+        if (code.code >= 10) {
+            super.write((code.code / 10) + '0');
+        }
+        super.write((code.code % 10) + '0');
+        super.write('m');
+    }
+
+    public static String decode(String str) throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        AnsiOutputStream aos = new AnsiOutputStream(baos);
+        aos.write(str.getBytes());
+        aos.close();
+        return baos.toString();
+    }
+
+}

Added: felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/commands/BlueprintCommand.java
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/commands/BlueprintCommand.java?rev=792445&view=auto
==============================================================================
--- felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/commands/BlueprintCommand.java (added)
+++ felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/commands/BlueprintCommand.java Thu Jul  9 08:29:14 2009
@@ -0,0 +1,90 @@
+/**
+ *
+ * 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.felix.karaf.gshell.console.commands;
+
+import java.lang.reflect.Type;
+import java.util.List;
+
+import org.apache.felix.gogo.commands.Action;
+import org.apache.felix.gogo.commands.basic.AbstractCommand;
+import org.apache.felix.gogo.commands.basic.ActionPreparator;
+import org.apache.felix.gogo.commands.basic.DefaultActionPreparator;
+import org.apache.felix.karaf.gshell.console.BlueprintContainerAware;
+import org.apache.felix.karaf.gshell.console.BundleContextAware;
+import org.apache.felix.karaf.gshell.console.CompletableFunction;
+import org.apache.felix.karaf.gshell.console.Completer;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.blueprint.container.BlueprintContainer;
+import org.osgi.service.blueprint.container.Converter;
+import org.osgi.service.command.CommandSession;
+
+public class BlueprintCommand extends AbstractCommand implements CompletableFunction
+{
+
+    protected BlueprintContainer blueprintContainer;
+    protected Converter blueprintConverter;
+    protected String actionId;
+    protected List<Completer> completers;
+
+    public void setBlueprintContainer(BlueprintContainer blueprintContainer) {
+        this.blueprintContainer = blueprintContainer;
+    }
+
+    public void setBlueprintConverter(Converter blueprintConverter) {
+        this.blueprintConverter = blueprintConverter;
+    }
+
+    public void setActionId(String actionId) {
+        this.actionId = actionId;
+    }
+
+    public List<Completer> getCompleters() {
+        return completers;
+    }
+
+    public void setCompleters(List<Completer> completers) {
+        this.completers = completers;
+    }
+
+    @Override
+    protected ActionPreparator getPreparator() throws Exception {
+        return new BlueprintActionPreparator();
+    }
+
+    class BlueprintActionPreparator extends DefaultActionPreparator {
+
+        @Override
+        protected Object convert(Action action, CommandSession commandSession, Object o, Type type) throws Exception {
+            return blueprintConverter.convert(o, new GenericType(type));
+        }
+
+    }
+
+    protected Action createNewAction() throws Exception {
+        Action action = (Action) blueprintContainer.getComponentInstance(actionId);
+        if (action instanceof BlueprintContainerAware) {
+            ((BlueprintContainerAware) action).setBlueprintContainer(blueprintContainer);
+        }
+        if (action instanceof BundleContextAware) {
+            BundleContext context = (BundleContext) blueprintContainer.getComponentInstance("blueprintBundleContext");
+            ((BundleContextAware) action).setBundleContext(context);
+        }
+        return action;
+    }
+
+}

Added: felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/commands/GenericType.java
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/commands/GenericType.java?rev=792445&view=auto
==============================================================================
--- felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/commands/GenericType.java (added)
+++ felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/commands/GenericType.java Thu Jul  9 08:29:14 2009
@@ -0,0 +1,195 @@
+/**
+ *
+ * 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.felix.karaf.gshell.console.commands;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.osgi.framework.Bundle;
+import org.osgi.service.blueprint.container.ReifiedType;
+
+public class GenericType extends ReifiedType {
+
+	private static final GenericType[] EMPTY = new GenericType[0];
+
+    private static final Map<String, Class> primitiveClasses = new HashMap<String, Class>();
+
+    static {
+        primitiveClasses.put("int", int.class);
+        primitiveClasses.put("short", short.class);
+        primitiveClasses.put("long", long.class);
+        primitiveClasses.put("byte", byte.class);
+        primitiveClasses.put("char", char.class);
+        primitiveClasses.put("float", float.class);
+        primitiveClasses.put("double", double.class);
+        primitiveClasses.put("boolean", boolean.class);
+    }
+
+    private GenericType[] parameters;
+
+	public GenericType(Type type) {
+		this(getConcreteClass(type), parametersOf(type));
+	}
+
+    public GenericType(Class clazz, GenericType... parameters) {
+        super(clazz);
+        this.parameters = parameters;
+    }
+
+    public static GenericType parse(String type, Object loader) throws ClassNotFoundException, IllegalArgumentException {
+        type = type.trim();
+        // Check if this is an array
+        if (type.endsWith("[]")) {
+            GenericType t = parse(type.substring(0, type.length() - 2), loader);
+            return new GenericType(Array.newInstance(t.getRawClass(), 0).getClass(), t);
+        }
+        // Check if this is a generic
+        int genericIndex = type.indexOf('<');
+        if (genericIndex > 0) {
+            if (!type.endsWith(">")) {
+                throw new IllegalArgumentException("Can not load type: " + type);
+            }
+            GenericType base = parse(type.substring(0, genericIndex), loader);
+            String[] params = type.substring(genericIndex + 1, type.length() - 1).split(",");
+            GenericType[] types = new GenericType[params.length];
+            for (int i = 0; i < params.length; i++) {
+                types[i] = parse(params[i], loader);
+            }
+            return new GenericType(base.getRawClass(), types);
+        }
+        // Primitive
+        if (primitiveClasses.containsKey(type)) {
+            return new GenericType(primitiveClasses.get(type));
+        }
+        // Class
+        if (loader instanceof ClassLoader) {
+            return new GenericType(((ClassLoader) loader).loadClass(type));
+        } else if (loader instanceof Bundle) {
+            return new GenericType(((Bundle) loader).loadClass(type));
+        } else {
+            throw new IllegalArgumentException("Unsupported loader: " + loader);
+        }
+    }
+
+    @Override
+    public ReifiedType getActualTypeArgument(int i) {
+        if (parameters.length == 0) {
+            return super.getActualTypeArgument(i);
+        }
+        return parameters[i];
+    }
+
+    @Override
+    public int size() {
+        return parameters.length;
+    }
+
+    @Override
+    public String toString() {
+        Class cl = getRawClass();
+        if (cl.isArray()) {
+            if (parameters.length > 0) {
+                return parameters[0].toString() + "[]";
+            } else {
+                return cl.getComponentType().getName() + "[]";
+            }
+        }
+        if (parameters.length > 0) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(cl.getName());
+            sb.append("<");
+            for (int i = 0; i < parameters.length; i++) {
+                if (i > 0) {
+                    sb.append(",");
+                }
+                sb.append(parameters[i].toString());
+            }
+            sb.append(">");
+            return sb.toString();
+        }
+        return cl.getName();
+    }
+
+    static GenericType[] parametersOf(Type type ) {
+		if ( type instanceof Class ) {
+		    Class clazz = (Class) type;
+		    if (clazz.isArray()) {
+                GenericType t = new GenericType(clazz.getComponentType());
+                if (t.size() > 0) {
+		            return new GenericType[] { t };
+                } else {
+                    return EMPTY;
+                }
+		    } else {
+		        return EMPTY;
+		    }
+		}
+        if ( type instanceof ParameterizedType ) {
+            ParameterizedType pt = (ParameterizedType) type;
+            Type [] parameters = pt.getActualTypeArguments();
+            GenericType[] gts = new GenericType[parameters.length];
+            for ( int i =0; i<gts.length; i++) {
+                gts[i] = new GenericType(parameters[i]);
+            }
+            return gts;
+        }
+        if ( type instanceof GenericArrayType ) {
+            return new GenericType[] { new GenericType(((GenericArrayType) type).getGenericComponentType()) };
+        }
+        throw new IllegalStateException();
+	}
+
+	static Class<?> getConcreteClass(Type type) {
+		Type ntype = collapse(type);
+		if ( ntype instanceof Class )
+			return (Class<?>) ntype;
+
+		if ( ntype instanceof ParameterizedType )
+			return getConcreteClass(collapse(((ParameterizedType)ntype).getRawType()));
+
+		throw new RuntimeException("Unknown type " + type );
+	}
+
+	static Type collapse(Type target) {
+		if (target instanceof Class || target instanceof ParameterizedType ) {
+			return target;
+		} else if (target instanceof TypeVariable) {
+			return collapse(((TypeVariable<?>) target).getBounds()[0]);
+		} else if (target instanceof GenericArrayType) {
+			Type t = collapse(((GenericArrayType) target)
+					.getGenericComponentType());
+			while ( t instanceof ParameterizedType )
+				t = collapse(((ParameterizedType)t).getRawType());
+			return Array.newInstance((Class<?>)t, 0).getClass();
+		} else if (target instanceof WildcardType) {
+			WildcardType wct = (WildcardType) target;
+			if (wct.getLowerBounds().length == 0)
+				return collapse(wct.getUpperBounds()[0]);
+			else
+				return collapse(wct.getLowerBounds()[0]);
+		}
+		throw new RuntimeException("Huh? " + target);
+	}
+
+}

Copied: felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/commands/NamespaceHandler.java (from r792359, felix/trunk/karaf/gshell/gshell-core/src/main/java/org/apache/felix/karaf/gshell/core/config/NamespaceHandler.java)
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/commands/NamespaceHandler.java?p2=felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/commands/NamespaceHandler.java&p1=felix/trunk/karaf/gshell/gshell-core/src/main/java/org/apache/felix/karaf/gshell/core/config/NamespaceHandler.java&r1=792359&r2=792445&rev=792445&view=diff
==============================================================================
--- felix/trunk/karaf/gshell/gshell-core/src/main/java/org/apache/felix/karaf/gshell/core/config/NamespaceHandler.java (original)
+++ felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/commands/NamespaceHandler.java Thu Jul  9 08:29:14 2009
@@ -16,7 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.felix.karaf.gshell.core.config;
+package org.apache.felix.karaf.gshell.console.commands;
+
 import java.net.URL;
 import java.util.List;
 
@@ -26,23 +27,12 @@
 
 import org.apache.geronimo.blueprint.ParserContext;
 import org.apache.geronimo.blueprint.mutable.MutableBeanMetadata;
-import org.apache.geronimo.blueprint.mutable.MutableCollectionMetadata;
 import org.apache.geronimo.blueprint.mutable.MutableIdRefMetadata;
 import org.apache.geronimo.blueprint.mutable.MutableServiceMetadata;
 import org.apache.geronimo.blueprint.mutable.MutableValueMetadata;
 import org.apache.geronimo.blueprint.mutable.MutableRefMetadata;
-import org.apache.geronimo.gshell.command.Alias;
-import org.apache.geronimo.gshell.command.Command;
-import org.apache.geronimo.gshell.command.Link;
-import org.apache.geronimo.gshell.wisdom.command.AliasImpl;
-import org.apache.geronimo.gshell.wisdom.command.CommandMessageSource;
-import org.apache.geronimo.gshell.wisdom.command.LinkImpl;
-import org.apache.geronimo.gshell.wisdom.command.MessageSourceCommandDocumenter;
-import org.apache.geronimo.gshell.wisdom.command.StatefulCommand;
-import org.apache.geronimo.gshell.wisdom.command.StatelessCommand;
-import org.apache.geronimo.gshell.wisdom.command.ConfigurableCommandCompleter;
-import org.apache.geronimo.gshell.wisdom.registry.CommandLocationImpl;
-import org.apache.felix.karaf.gshell.core.BeanContainerWrapper;
+import org.apache.geronimo.blueprint.mutable.MutableCollectionMetadata;
+import org.apache.felix.karaf.gshell.console.CompletableFunction;
 import org.osgi.service.blueprint.reflect.BeanArgument;
 import org.osgi.service.blueprint.reflect.BeanProperty;
 import org.osgi.service.blueprint.reflect.ComponentMetadata;
@@ -53,7 +43,7 @@
 import org.osgi.service.blueprint.reflect.NullMetadata;
 import org.osgi.service.blueprint.reflect.BeanMetadata;
 import org.osgi.service.blueprint.container.ComponentDefinitionException;
-import org.osgi.service.blueprint.container.BlueprintContainer;
+import org.osgi.service.command.Function;
 
 
 public class NamespaceHandler implements org.apache.geronimo.blueprint.NamespaceHandler {
@@ -69,7 +59,6 @@
     public static final String LOCATION = "location";
     public static final String COMMANDS = "commands";
     public static final String COMMAND = "command";
-    public static final String TYPE = "type";
     public static final String DOCUMENTER = "documenter";
     public static final String COMPLETER = "completer";
     public static final String COMPLETERS = "completers";
@@ -84,14 +73,13 @@
     public static final String LINK = "link";
     public static final String LINKS = "links";
     public static final String TARGET = "target";
-
-    public static final String TYPE_STATEFUL = "stateful";
-    public static final String TYPE_STATELESS = "stateless";
+    public static final String BLUEPRINT_CONTAINER = "blueprintContainer";
+    public static final String BLUEPRINT_CONVERTER = "blueprintConverter";
 
     private int nameCounter = 0;
 
     public URL getSchemaLocation(String namespace) {
-        return getClass().getResource("/org/apache/felix/karaf/gshell/core/karaf-gshell.xsd");
+        return getClass().getResource("karaf-gshell.xsd");
     }
 
     public ComponentMetadata decorate(Node node, ComponentMetadata component, ParserContext context) {
@@ -118,62 +106,52 @@
         if (nodeNameEquals(element, COMMAND)) {
             parseCommand(element, context);
         } else if (nodeNameEquals(element, LINK)) {
-            parseLink(element, context);
+//            parseLink(element, context);
         } else if (nodeNameEquals(element, ALIAS)) {
-            parseAlias(element, context);
+//            parseAlias(element, context);
         }
     }
 
     private void parseCommand(Element element, ParserContext context) {
         MutableBeanMetadata command = context.createMetadata(MutableBeanMetadata.class);
-        String type = element.hasAttribute(TYPE) ? element.getAttribute(TYPE) : TYPE_STATEFUL;
-        boolean stateful;
-        if (TYPE_STATEFUL.equals(type)) {
-            command.setRuntimeClass(StatefulCommand.class);
-            stateful = true;
-        } else if (TYPE_STATELESS.equals(type)) {
-            command.setRuntimeClass(StatelessCommand.class);
-            stateful = false;
+        command.setRuntimeClass(BlueprintCommand.class);
+        command.addProperty(BLUEPRINT_CONTAINER, createRef(context, BLUEPRINT_CONTAINER));
+        command.addProperty(BLUEPRINT_CONVERTER, createRef(context, BLUEPRINT_CONVERTER));
+//        MutableBeanMetadata documenter = context.createMetadata(MutableBeanMetadata.class);
+//        documenter.setRuntimeClass(MessageSourceCommandDocumenter.class);
+//        command.addProperty(DOCUMENTER, documenter);
+//        MutableBeanMetadata messages = context.createMetadata(MutableBeanMetadata.class);
+//        messages.setRuntimeClass(CommandMessageSource.class);
+//        command.addProperty(MESSAGES, messages);
+//        MutableBeanMetadata location = context.createMetadata(MutableBeanMetadata.class);
+//        location.setRuntimeClass(CommandLocationImpl.class);
+//        location.addArgument(createStringValue(context, element.getAttribute(NAME)), String.class.getName(), 0);
+//        command.addProperty(LOCATION, location);
+
+        String location = element.getAttribute(NAME);
+        location = location.replace('/', ':');
+        String scope;
+        String function;
+        if (location.lastIndexOf(':') >= 0) {
+            scope = location.substring(0, location.lastIndexOf(':'));
+            function = location.substring(location.lastIndexOf(':') + 1);
         } else {
-            throw new ComponentDefinitionException("Bad xml syntax: unknown value '" + type + "' for attribute " + TYPE);
+            scope = "";
+            function = location;
         }
-        MutableBeanMetadata beanContainer = context.createMetadata(MutableBeanMetadata.class);
-        beanContainer.setRuntimeClass(BeanContainerWrapper.class);
-        beanContainer.addArgument(createRef(context, "blueprintContainer"), BlueprintContainer.class.getName(), 0);
-        command.addProperty("beanContainer", beanContainer);
-        MutableBeanMetadata documenter = context.createMetadata(MutableBeanMetadata.class);
-        documenter.setRuntimeClass(MessageSourceCommandDocumenter.class);
-        command.addProperty(DOCUMENTER, documenter);
-        MutableBeanMetadata messages = context.createMetadata(MutableBeanMetadata.class);
-        messages.setRuntimeClass(CommandMessageSource.class);
-        command.addProperty(MESSAGES, messages);
-        MutableBeanMetadata location = context.createMetadata(MutableBeanMetadata.class);
-        location.setRuntimeClass(CommandLocationImpl.class);
-        location.addArgument(createStringValue(context, element.getAttribute(NAME)), String.class.getName(), 0);
-        command.addProperty(LOCATION, location);
 
-        boolean hasAction = false;
         NodeList children = element.getChildNodes();
         for (int i = 0; i < children.getLength(); i++) {
             Node child  = children.item(i);
             if (child instanceof Element) {
                 Element childElement = (Element) child;
                 if (nodeNameEquals(childElement, ACTION)) {
-                    if (hasAction) {
-                        throw new ComponentDefinitionException("Only one " + ACTION + " element can be set for a given command");
-                    }
-                    hasAction = true;
                     MutableBeanMetadata action = parseAction(context, command, childElement);
-                    // TODO: parse other stuff or remove it from schema
-                    if (stateful) {
-                        action.setId(getName());
-                        context.getComponentDefinitionRegistry().registerComponentDefinition(action);
-                        command.addProperty(ACTION_ID, createIdRef(context, action.getId()));
-                    } else {
-                        command.addProperty(ACTION, action);
-                    }
+                    action.setId(getName());
+                    context.getComponentDefinitionRegistry().registerComponentDefinition(action);
+                    command.addProperty(ACTION_ID, createIdRef(context, action.getId()));
                 } else if (nodeNameEquals(childElement, COMPLETERS)) {
-                    command.addProperty(COMPLETER, parseCompleters(context, command, childElement));
+                    command.addProperty(COMPLETERS, parseCompleters(context, command, childElement));
                 } else {
                     throw new ComponentDefinitionException("Bad xml syntax: unknown element '" + childElement.getNodeName() + "'");
                 }
@@ -182,8 +160,13 @@
 
         MutableServiceMetadata commandService = context.createMetadata(MutableServiceMetadata.class);
         commandService.setId(getName());
-        commandService.addInterface(Command.class.getName());
+        commandService.addInterface(Function.class.getName());
+        commandService.addInterface(CompletableFunction.class.getName());
         commandService.setServiceComponent(command);
+        commandService.addServiceProperty(createStringValue(context, "osgi.command.scope"),
+                                          createStringValue(context, scope));
+        commandService.addServiceProperty(createStringValue(context, "osgi.command.function"),
+                                          createStringValue(context, function));
         context.getComponentDefinitionRegistry().registerComponentDefinition(commandService);
     }
 
@@ -208,8 +191,6 @@
     }
 
     private Metadata parseCompleters(ParserContext context, ComponentMetadata enclosingComponent, Element element) {
-        MutableBeanMetadata completer = context.createMetadata(MutableBeanMetadata.class);
-        completer.setRuntimeClass(ConfigurableCommandCompleter.class);
         MutableCollectionMetadata collection = context.createMetadata(MutableCollectionMetadata.class);
         collection.setCollectionClass(List.class);
         NodeList children = element.getChildNodes();
@@ -229,36 +210,35 @@
                 collection.addValue(metadata);
             }
         }
-        completer.addArgument(collection, List.class.getName(), 0);
-        return completer;
-    }
-
-    private void parseLink(Element element, ParserContext context) {
-        MutableBeanMetadata link = context.createMetadata(MutableBeanMetadata.class);
-        link.setRuntimeClass(LinkImpl.class);
-        link.addArgument(createStringValue(context, element.getAttribute(NAME)), String.class.getName(), 0);
-        link.addArgument(createStringValue(context, element.getAttribute(TARGET)), String.class.getName(), 0);
-
-        MutableServiceMetadata linkService = context.createMetadata(MutableServiceMetadata.class);
-        linkService.setId(getName());
-        linkService.addInterface(Link.class.getName());
-        linkService.setServiceComponent(link);
-        context.getComponentDefinitionRegistry().registerComponentDefinition(linkService);
-    }
-
-    private void parseAlias(Element element, ParserContext context) {
-        MutableBeanMetadata alias = context.createMetadata(MutableBeanMetadata.class);
-        alias.setRuntimeClass(AliasImpl.class);
-        alias.addArgument(createStringValue(context, element.getAttribute(NAME)), String.class.getName(), 0);
-        alias.addArgument(createStringValue(context, element.getAttribute(ALIAS)), String.class.getName(), 0);
-
-        MutableServiceMetadata aliasService = context.createMetadata(MutableServiceMetadata.class);
-        aliasService.setId(getName());
-        aliasService.addInterface(Alias.class.getName());
-        aliasService.setServiceComponent(alias);
-        context.getComponentDefinitionRegistry().registerComponentDefinition(aliasService);
+        return collection;
     }
 
+//    private void parseLink(Element element, ParserContext context) {
+//        MutableBeanMetadata link = context.createMetadata(MutableBeanMetadata.class);
+//        link.setRuntimeClass(LinkImpl.class);
+//        link.addArgument(createStringValue(context, element.getAttribute(NAME)), String.class.getName(), 0);
+//        link.addArgument(createStringValue(context, element.getAttribute(TARGET)), String.class.getName(), 0);
+//
+//        MutableServiceMetadata linkService = context.createMetadata(MutableServiceMetadata.class);
+//        linkService.setId(getName());
+//        linkService.addInterface(Link.class.getName());
+//        linkService.setServiceComponent(link);
+//        context.getComponentDefinitionRegistry().registerComponentDefinition(linkService);
+//    }
+//
+//    private void parseAlias(Element element, ParserContext context) {
+//        MutableBeanMetadata alias = context.createMetadata(MutableBeanMetadata.class);
+//        alias.setRuntimeClass(AliasImpl.class);
+//        alias.addArgument(createStringValue(context, element.getAttribute(NAME)), String.class.getName(), 0);
+//        alias.addArgument(createStringValue(context, element.getAttribute(ALIAS)), String.class.getName(), 0);
+//
+//        MutableServiceMetadata aliasService = context.createMetadata(MutableServiceMetadata.class);
+//        aliasService.setId(getName());
+//        aliasService.addInterface(Alias.class.getName());
+//        aliasService.setServiceComponent(alias);
+//        context.getComponentDefinitionRegistry().registerComponentDefinition(aliasService);
+//    }
+//
     private ValueMetadata createStringValue(ParserContext context, String str) {
         MutableValueMetadata value = context.createMetadata(MutableValueMetadata.class);
         value.setStringValue(str);

Added: felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/completer/AggregateCompleter.java
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/completer/AggregateCompleter.java?rev=792445&view=auto
==============================================================================
--- felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/completer/AggregateCompleter.java (added)
+++ felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/completer/AggregateCompleter.java Thu Jul  9 08:29:14 2009
@@ -0,0 +1,91 @@
+/*
+ * 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.felix.karaf.gshell.console.completer;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Collection;
+
+import org.apache.felix.karaf.gshell.console.Completer;
+
+/**
+ * Completer which contains multipule completers and aggregates them together.
+ *
+ * @version $Rev: 699733 $ $Date: 2008-09-27 22:55:23 +0200 (Sat, 27 Sep 2008) $
+ */
+public class AggregateCompleter implements Completer
+{
+    private final Collection<Completer> completers;
+
+    public AggregateCompleter(final Collection<Completer> completers) {
+        assert completers != null;
+        this.completers = completers;
+    }
+
+    public int complete(final String buffer, final int cursor, final List candidates) {
+        // buffer could be null
+        assert candidates != null;
+
+        List<Completion> completions = new ArrayList<Completion>(completers.size());
+
+        // Run each completer, saving its completion results
+        int max = -1;
+        for (Completer completer : completers) {
+            Completion completion = new Completion(candidates);
+            completion.complete(completer, buffer, cursor);
+
+            // Compute the max cursor position
+            max = Math.max(max, completion.cursor);
+
+            completions.add(completion);
+        }
+
+        // Append candiates from completions which have the same cursor position as max
+        for (Completion completion : completions) {
+            if (completion.cursor == max) {
+                // noinspection unchecked
+                candidates.addAll(completion.candidates);
+            }
+        }
+
+        return max;
+    }
+
+    private class Completion
+    {
+        public final List<String> candidates;
+
+        public int cursor;
+
+        public Completion(final List candidates) {
+            assert candidates != null;
+
+            // noinspection unchecked
+            this.candidates = new LinkedList<String>(candidates);
+        }
+
+        public void complete(final Completer completer, final String buffer, final int cursor) {
+            assert completer != null;
+
+            this.cursor = completer.complete(buffer, cursor, candidates);
+        }
+    }
+}

Added: felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/completer/ArgumentCompleter.java
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/completer/ArgumentCompleter.java?rev=792445&view=auto
==============================================================================
--- felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/completer/ArgumentCompleter.java (added)
+++ felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/completer/ArgumentCompleter.java Thu Jul  9 08:29:14 2009
@@ -0,0 +1,393 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package org.apache.felix.karaf.gshell.console.completer;
+
+import java.util.*;
+
+import org.apache.felix.karaf.gshell.console.Completer;
+
+public class ArgumentCompleter implements Completer {
+    final Completer[] completers;
+    final ArgumentDelimiter delim;
+    boolean strict = true;
+
+    /**
+     *  Constuctor: create a new completor with the default
+     *  argument separator of " ".
+     *
+     *  @param  completer  the embedded completer
+     */
+    public ArgumentCompleter(final Completer completer) {
+        this(new Completer[] {
+                 completer
+             });
+    }
+
+    /**
+     *  Constuctor: create a new completor with the default
+     *  argument separator of " ".
+     *
+     *  @param  completers  the List of completors to use
+     */
+    public ArgumentCompleter(final List<Completer> completers) {
+        this(completers.toArray(new Completer[completers.size()]));
+    }
+
+    /**
+     *  Constuctor: create a new completor with the default
+     *  argument separator of " ".
+     *
+     *  @param  completers  the embedded argument completers
+     */
+    public ArgumentCompleter(final Completer[] completers) {
+        this(completers, new WhitespaceArgumentDelimiter());
+    }
+
+    /**
+     *  Constuctor: create a new completor with the specified
+     *  argument delimiter.
+     *
+     *  @param  completer the embedded completer
+     *  @param  delim     the delimiter for parsing arguments
+     */
+    public ArgumentCompleter(final Completer completer,
+                             final ArgumentDelimiter delim) {
+        this(new Completer[] {
+                 completer
+             }, delim);
+    }
+
+    /**
+     *  Constuctor: create a new completor with the specified
+     *  argument delimiter.
+     *
+     *  @param  completers the embedded completers
+     *  @param  delim      the delimiter for parsing arguments
+     */
+    public ArgumentCompleter(final Completer[] completers,
+                             final ArgumentDelimiter delim) {
+        this.completers = completers;
+        this.delim = delim;
+    }
+
+    /**
+     *  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 String buffer, final int cursor,
+                        final List<String> candidates) {
+        ArgumentList list = delim.delimit(buffer, cursor);
+        int argpos = list.getArgumentPosition();
+        int argIndex = list.getCursorArgumentIndex();
+
+        if (argIndex < 0) {
+            return -1;
+        }
+
+        final Completer comp;
+
+        // if we are beyond the end of the completors, just use the last one
+        if (argIndex >= completers.length) {
+            comp = completers[completers.length - 1];
+        } else {
+            comp = completers[argIndex];
+        }
+
+        // ensure that all the previous completors are successful before
+        // allowing this completor to pass (only if strict is true).
+        for (int i = 0; getStrict() && (i < argIndex); i++) {
+            Completer sub = completers[(i >= completers.length) ? (completers.length - 1) : i];
+            String[] args = list.getArguments();
+            String arg = ((args == null) || (i >= args.length)) ? "" : args[i];
+
+            List<String> subCandidates = new LinkedList<String>();
+
+            if (sub.complete(arg, arg.length(), subCandidates) == -1) {
+                return -1;
+            }
+
+            if (subCandidates.size() == 0) {
+                return -1;
+            }
+        }
+
+        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".
+         */
+        if ((cursor != buffer.length()) && delim.isDelimiter(buffer, cursor)) {
+            for (int i = 0; i < candidates.size(); i++) {
+                String val = candidates.get(i);
+
+                while ((val.length() > 0)
+                    && delim.isDelimiter(val, val.length() - 1)) {
+                    val = val.substring(0, val.length() - 1);
+                }
+
+                candidates.set(i, val);
+            }
+        }
+
+        return pos;
+    }
+
+    /**
+     *  The {@link ArgumentCompleter.ArgumentDelimiter} allows custom
+     *  breaking up of a {@link String} into individual arguments in
+     *  order to dispatch the arguments to the nested {@link Completer}.
+     *
+     *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+     */
+    public static interface ArgumentDelimiter {
+        /**
+         *  Break the specified buffer into individual tokens
+         *  that can be completed on their own.
+         *
+         *  @param  buffer           the buffer to split
+         *  @param  argumentPosition the current position of the
+         *                           cursor in the buffer
+         *  @return                  the tokens
+         */
+        ArgumentList delimit(String buffer, int argumentPosition);
+
+        /**
+         *  Returns true if the specified character is a whitespace
+         *  parameter.
+         *
+         *  @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
+         */
+        boolean isDelimiter(String buffer, int pos);
+    }
+
+    /**
+     *  Abstract implementation of a delimiter that uses the
+     *  {@link #isDelimiter} method to determine if a particular
+     *  character should be used as a delimiter.
+     *
+     *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+     */
+    public abstract static class AbstractArgumentDelimiter
+        implements ArgumentDelimiter {
+        private char[] quoteChars = new char[] { '\'', '"' };
+        private char[] escapeChars = new char[] { '\\' };
+
+        public void setQuoteChars(final char[] quoteChars) {
+            this.quoteChars = quoteChars;
+        }
+
+        public char[] getQuoteChars() {
+            return this.quoteChars;
+        }
+
+        public void setEscapeChars(final char[] escapeChars) {
+            this.escapeChars = escapeChars;
+        }
+
+        public char[] getEscapeChars() {
+            return this.escapeChars;
+        }
+
+        public ArgumentList delimit(final String buffer, final int cursor) {
+            List<String> args = new LinkedList<String>();
+            StringBuffer arg = new StringBuffer();
+            int argpos = -1;
+            int bindex = -1;
+
+            for (int i = 0; (buffer != null) && (i <= buffer.length()); i++) {
+                // once we reach the cursor, set the
+                // position of the selected index
+                if (i == cursor) {
+                    bindex = args.size();
+                    // the position in the current argument is just the
+                    // length of the current argument
+                    argpos = arg.length();
+                }
+
+                if ((i == buffer.length()) || isDelimiter(buffer, i)) {
+                    if (arg.length() > 0) {
+                        args.add(arg.toString());
+                        arg.setLength(0); // reset the arg
+                    }
+                } else {
+                    arg.append(buffer.charAt(i));
+                }
+            }
+
+            return new ArgumentList(args.
+                toArray(new String[args.size()]), bindex, argpos, cursor);
+        }
+
+        /**
+         *  Returns true if the specified character is a whitespace
+         *  parameter. Check to ensure that the character is not
+         *  escaped by any of
+         *  {@link #getQuoteChars}, and is not escaped by ant of the
+         *  {@link #getEscapeChars}, 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) {
+            if (isQuoted(buffer, pos)) {
+                return false;
+            }
+
+            if (isEscaped(buffer, pos)) {
+                return false;
+            }
+
+            return isDelimiterChar(buffer, pos);
+        }
+
+        public boolean isQuoted(final String buffer, final int pos) {
+            return false;
+        }
+
+        public boolean isEscaped(final String buffer, final int pos) {
+            if (pos <= 0) {
+                return false;
+            }
+
+            for (int i = 0; (escapeChars != null) && (i < escapeChars.length);
+                     i++) {
+                if (buffer.charAt(pos) == escapeChars[i]) {
+                    return !isEscaped(buffer, pos - 1); // escape escape
+                }
+            }
+
+            return false;
+        }
+
+        /**
+         *  Returns true if the character at the specified position
+         *  if a delimiter. This method will only be called if the
+         *  character is not enclosed in any of the
+         *  {@link #getQuoteChars}, and is not escaped by ant of the
+         *  {@link #getEscapeChars}. To perform escaping manually,
+         *  override {@link #isDelimiter} instead.
+         */
+        public abstract boolean isDelimiterChar(String buffer, int pos);
+    }
+
+    /**
+     *  {@link ArgumentCompleter.ArgumentDelimiter}
+     *  implementation that counts all
+     *  whitespace (as reported by {@link Character#isWhitespace})
+     *  as being a delimiter.
+     *
+     *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+     */
+    public static class WhitespaceArgumentDelimiter
+        extends AbstractArgumentDelimiter {
+        /**
+         *  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.
+     *
+     *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+     */
+    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;
+        }
+    }
+}

Added: felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/completer/CommandsCompleter.java
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/completer/CommandsCompleter.java?rev=792445&view=auto
==============================================================================
--- felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/completer/CommandsCompleter.java (added)
+++ felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/completer/CommandsCompleter.java Thu Jul  9 08:29:14 2009
@@ -0,0 +1,86 @@
+package org.apache.felix.karaf.gshell.console.completer;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.felix.karaf.gshell.console.Completer;
+import org.apache.felix.karaf.gshell.console.CompletableFunction;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.command.CommandProcessor;
+
+public class CommandsCompleter implements Completer {
+
+    private final Map<ServiceReference, Completer> completers = new ConcurrentHashMap<ServiceReference, Completer>();
+
+    private BundleContext bundleContext;
+
+    public void setBundleContext(BundleContext bundleContext) {
+        this.bundleContext = bundleContext;
+    }
+
+    public void register(ServiceReference reference) {
+        Set<String> functions = getNames(reference);
+        if (functions != null) {
+            List<Completer> cl = new ArrayList<Completer>();
+            cl.add(new StringsCompleter(functions));
+            try {
+                Object function = bundleContext.getService(reference);
+                if (function instanceof CompletableFunction) {
+                    List<Completer> fcl = ((CompletableFunction) function).getCompleters();
+                    if (fcl != null) {
+                        for (Completer c : fcl) {
+                            cl.add(c == null ? NullCompleter.INSTANCE : c);
+                        }
+                    } else {
+                        cl.add(NullCompleter.INSTANCE);
+                    }
+                } else {
+                    cl.add(NullCompleter.INSTANCE);
+                }
+            } finally {
+                bundleContext.ungetService(reference);
+            }
+            ArgumentCompleter c = new ArgumentCompleter(cl);
+            completers.put(reference, c);
+        }
+    }
+
+    public void unregister(ServiceReference reference) {
+        completers.remove(reference);
+    }
+
+    private Set<String> getNames(ServiceReference reference) {
+        Set<String> names = new HashSet<String>();
+        Object scope = reference.getProperty(CommandProcessor.COMMAND_SCOPE);
+        Object function = reference.getProperty(CommandProcessor.COMMAND_FUNCTION);
+        if(scope != null && function != null)
+        {
+            if (function.getClass().isArray())
+            {
+                for (Object f : ((Object[]) function))
+                {
+                    names.add(scope + ":" + f.toString());
+                }
+            }
+            else
+            {
+                names.add(scope + ":" + function.toString());
+            }
+            return names;
+        }
+        return null;
+    }
+
+    public int complete(String buffer, int cursor, List<String> candidates) {
+        int res =  new AggregateCompleter(completers.values()).complete(buffer, cursor, candidates);
+        Collections.sort(candidates);
+        return res;
+    }
+}
+

Added: felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/completer/NullCompleter.java
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/completer/NullCompleter.java?rev=792445&view=auto
==============================================================================
--- felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/completer/NullCompleter.java (added)
+++ felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/completer/NullCompleter.java Thu Jul  9 08:29:14 2009
@@ -0,0 +1,14 @@
+package org.apache.felix.karaf.gshell.console.completer;
+
+import java.util.List;
+
+import org.apache.felix.karaf.gshell.console.Completer;
+
+public class NullCompleter implements Completer {
+
+    public static final NullCompleter INSTANCE = new NullCompleter();
+
+    public int complete(String buffer, int cursor, List<String> candidates) {
+        return -1;
+    }
+}

Added: felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/completer/StringsCompleter.java
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/completer/StringsCompleter.java?rev=792445&view=auto
==============================================================================
--- felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/completer/StringsCompleter.java (added)
+++ felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/completer/StringsCompleter.java Thu Jul  9 08:29:14 2009
@@ -0,0 +1,81 @@
+/*
+ * 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.felix.karaf.gshell.console.completer;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.apache.felix.karaf.gshell.console.Completer;
+
+/**
+ * Completer for a set of strings.
+ *
+ * @version $Rev: 722797 $ $Date: 2008-12-03 08:18:16 +0100 (Wed, 03 Dec 2008) $
+ */
+public class StringsCompleter
+    implements Completer
+{
+    private final SortedSet<String> strings = new TreeSet<String>();
+
+    public StringsCompleter() {}
+
+    public StringsCompleter(final Collection<String> strings) {
+        assert strings != null;
+
+        getStrings().addAll(strings);
+    }
+
+    public StringsCompleter(final String[] strings) {
+        this(Arrays.asList(strings));
+    }
+
+    public Collection<String> getStrings() {
+        return strings;
+    }
+
+    public int complete(String buffer, final int cursor, final List candidates) {
+        // buffer could be null
+        assert candidates != null;
+
+        if (buffer == null) {
+            buffer = "";
+        }
+
+        SortedSet<String> matches = strings.tailSet(buffer);
+
+        for (String match : matches) {
+            if (!match.startsWith(buffer)) {
+                break;
+            }
+
+            // noinspection unchecked
+            candidates.add(match);
+        }
+
+        if (candidates.size() == 1) {
+            // noinspection unchecked
+            candidates.set(0, candidates.get(0) + " ");
+        }
+
+        return candidates.isEmpty() ? -1 : 0;
+    }
+}
\ No newline at end of file

Added: felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/jline/CompleterAsCompletor.java
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/jline/CompleterAsCompletor.java?rev=792445&view=auto
==============================================================================
--- felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/jline/CompleterAsCompletor.java (added)
+++ felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/jline/CompleterAsCompletor.java Thu Jul  9 08:29:14 2009
@@ -0,0 +1,26 @@
+package org.apache.felix.karaf.gshell.console.jline;
+
+import java.util.List;
+
+import jline.Completor;
+import org.apache.felix.karaf.gshell.console.Completer;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: gnodet
+ * Date: Jul 6, 2009
+ * Time: 10:26:15 AM
+ * To change this template use File | Settings | File Templates.
+ */
+public class CompleterAsCompletor implements Completor {
+
+    private final Completer completer;
+
+    public CompleterAsCompletor(Completer completer) {
+        this.completer = completer;
+    }
+
+    public int complete(String buffer, int cursor, List candidates) {
+        return completer.complete(buffer, cursor, candidates);
+    }
+}

Added: felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/jline/Console.java
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/jline/Console.java?rev=792445&view=auto
==============================================================================
--- felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/jline/Console.java (added)
+++ felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/jline/Console.java Thu Jul  9 08:29:14 2009
@@ -0,0 +1,259 @@
+/*
+ * 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.felix.karaf.gshell.console.jline;
+
+import jline.*;
+import org.osgi.service.command.CommandSession;
+import org.osgi.service.command.Converter;
+import org.apache.felix.karaf.gshell.console.ansi.AnsiOutputStream;
+import org.apache.felix.karaf.gshell.console.Completer;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.PrintWriter;
+import java.lang.reflect.Method;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.Properties;
+
+public class Console implements Runnable
+{
+
+    public static final String PROMPT = "PROMPT";
+    public static final String DEFAULT_PROMPT = "\"@|bold ${USER}|@${APPLICATION}:@|bold ${SCOPE}|> \"";
+
+    private CommandSession session;
+    private ConsoleReader reader;
+    private BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(1024);
+    private boolean interrupt;
+    private Thread pipe;
+    private boolean running;
+    private Runnable closeCallback;
+    private Terminal terminal;
+
+    public Console(CommandSession session, Terminal term) throws Exception
+    {
+        this(session, term, null);
+    }
+
+    public Console(CommandSession session, Terminal term, Completer completer) throws Exception
+    {
+        this(session, term, completer, null);
+    }
+
+    public Console(CommandSession session, Terminal term, Completer completer, Runnable closeCallback) throws Exception
+    {
+        this.session = session;
+        this.terminal = term == null ? new UnsupportedTerminal() : term;
+        this.closeCallback = closeCallback;
+        reader = new ConsoleReader(new ConsoleInputStream(),
+                                   new PrintWriter(session.getConsole()),
+                                   getClass().getResourceAsStream("keybinding.properties"),
+                                   terminal);
+        if (completer != null) {
+            reader.addCompletor(new CompleterAsCompletor(completer));
+        }
+        pipe = new Thread(new Pipe());
+        pipe.setName("gogo shell pipe thread");
+        pipe.setDaemon(true);
+    }
+
+    public void close() {
+        //System.err.println("Closing");
+        running = false;
+        pipe.interrupt();
+        Thread.interrupted();
+    }
+
+    public void run()
+    {
+        running = true;
+        pipe.start();
+        welcome();
+        while (running) {
+            try {
+                String prompt = AnsiOutputStream.decode(getPrompt());
+                String line = reader.readLine(prompt);
+                if (line == null)
+                {
+                    break;
+                }
+                //session.getConsole().println("Executing: " + line);
+                Object result = session.execute(line);
+                if (result != null)
+                {
+                    session.getConsole().println(session.format(result, Converter.INSPECT));
+                }
+            }
+            catch (InterruptedIOException e)
+            {
+                //System.err.println("^C");
+                // TODO: interrupt current thread
+            }
+            catch (Throwable t)
+            {
+                t.printStackTrace(session.getConsole());
+                //System.err.println("Exception: " + t.getMessage());
+            }
+        }
+        //System.err.println("Exiting console...");
+        if (closeCallback != null)
+        {
+            closeCallback.run();
+        }
+    }
+
+    protected void welcome() {
+        Properties props = new Properties();
+        loadProps(props, "/org/apache/felix/karaf/gshell/console/branding.properties");
+        loadProps(props, "/org/apache/felix/karaf/branding/branding.properties");
+        String welcome = props.getProperty("welcome");
+        if (welcome != null && welcome.length() > 0) {
+            try {
+                session.getConsole().println(AnsiOutputStream.decode(welcome));
+            } catch (IOException e) {
+                //
+            }
+        }
+    }
+
+    private void loadProps(Properties props, String resource) {
+        InputStream is = null;
+        try {
+            is = getClass().getClassLoader().getResourceAsStream(resource);
+            if (is != null) {
+                props.load(is);
+            }
+        } catch (IOException e) {
+            // ignore
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                    // Ignore
+                }
+            }
+        }
+    }
+
+    protected String getPrompt() {
+        try {
+            String prompt;
+            try {
+                Object p = session.get(PROMPT);
+                prompt = p != null ? p.toString() : DEFAULT_PROMPT;
+            } catch (Throwable t) {
+                prompt = DEFAULT_PROMPT;
+            }
+            Object v = session.execute(prompt);
+            if (v != null) {
+                prompt = v.toString();
+            }
+            return prompt;
+        } catch (Throwable t) {
+            return "$ ";
+        }
+    }
+
+    private void checkInterrupt() throws IOException {
+        if (interrupt) {
+            interrupt = false;
+            throw new InterruptedIOException("Keyboard interruption");
+        }
+    }
+
+    private void interrupt() {
+        interrupt = true;
+        //System.err.println("Interrupt ^C");
+    }
+
+    private class ConsoleInputStream extends InputStream
+    {
+        @Override
+        public int read() throws IOException
+        {
+            if (!running)
+            {
+                return -1;
+            }
+            checkInterrupt();
+            int i;
+            try {
+                i = queue.take();
+            }
+            catch (InterruptedException e)
+            {
+                throw new InterruptedIOException();
+            }
+            if (i == 4)
+            {
+                return -1;
+            }
+            checkInterrupt();
+            return i;
+        }
+    }
+
+    private class Pipe implements Runnable
+    {
+        public void run()
+        {
+            try {
+                InputStream in = session.getKeyboard();
+                while (running)
+                {
+                    try
+                    {
+                        int c = in.read();
+                        if (c == -1 || c == 4)
+                        {
+                            //System.err.println("Received  " + c + " ... closing");
+                            session.getConsole().println("^D");
+                            queue.put(c);
+                            return;
+                        }
+                        else if (c == 3)
+                        {
+                            session.getConsole().println("^C");
+                            reader.getCursorBuffer().clearBuffer();
+                            interrupt();
+                            queue.put(c);
+                        }
+                        else
+                        {
+                            queue.put(c);
+                        }
+                    }
+                    catch (Throwable t) {
+                        //System.err.println("Exception in pipe: " + t);
+                        return;
+                    }
+                }
+            }
+            finally
+            {
+                //System.err.println("Exiting pipe thread");
+                close();
+            }
+        }
+    }
+
+}

Added: felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/jline/ConsoleFactory.java
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/jline/ConsoleFactory.java?rev=792445&view=auto
==============================================================================
--- felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/jline/ConsoleFactory.java (added)
+++ felix/trunk/karaf/gshell/gshell-console/src/main/java/org/apache/felix/karaf/gshell/console/jline/ConsoleFactory.java Thu Jul  9 08:29:14 2009
@@ -0,0 +1,106 @@
+/*
+ * 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.felix.karaf.gshell.console.jline;
+
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.lang.reflect.Method;
+import java.util.List;
+
+import org.apache.felix.karaf.gshell.console.ansi.AnsiOutputStream;
+import org.apache.felix.karaf.gshell.console.Completer;
+import org.apache.felix.karaf.gshell.console.completer.AggregateCompleter;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.command.CommandProcessor;
+import org.osgi.service.command.CommandSession;
+import jline.Terminal;
+
+public class ConsoleFactory {
+
+    private BundleContext bundleContext;
+    private CommandProcessor commandProcessor;
+    private List<Completer> completers;
+    private Terminal terminal;
+    private Console console;
+    private boolean start;
+
+    public void setBundleContext(BundleContext bundleContext) {
+        this.bundleContext = bundleContext;
+    }
+
+    public synchronized void registerCommandProcessor(CommandProcessor commandProcessor) throws Exception {
+        this.commandProcessor = commandProcessor;
+        start();
+    }
+
+    public synchronized void unregisterCommandProcessor(CommandProcessor commandProcessor) throws Exception {
+        this.commandProcessor = null;
+        stop();
+    }
+
+    public void setCompleters(List<Completer> completers) {
+        this.completers = completers;
+    }
+
+    public void setTerminal(Terminal terminal) {
+        this.terminal = terminal;
+    }
+
+    public void setStart(boolean start) {
+        this.start = start;
+    }
+
+    protected void start() throws Exception {
+        if (start) {
+            InputStream in = unwrap(System.in);
+            PrintStream out = unwrap(System.out);
+            PrintStream err = unwrap(System.err);
+            CommandSession session = this.commandProcessor.createSession(
+                    in, new PrintStream(new AnsiOutputStream(out)), new PrintStream(new AnsiOutputStream(err)));
+            session.put("USER", "karaf");
+            session.put("APPLICATION", System.getProperty("karaf.name", "root"));
+            Runnable callback = new Runnable() {
+                public void run() {
+                    try {
+                        bundleContext.getBundle(0).stop();
+                    } catch (Exception e) {
+                        // Ignore
+                    }
+                }
+            };
+            this.console = new Console(session, terminal, new AggregateCompleter(completers), callback);
+            new Thread(console, "Karaf Shell Console Thread").start();
+        }
+    }
+
+    protected void stop() throws Exception {
+        if (console != null) {
+            console.close();
+        }
+    }
+
+    private static <T> T unwrap(T stream) {
+        try {
+            Method mth = stream.getClass().getMethod("getRoot");
+            return (T) mth.invoke(stream);
+        } catch (Throwable t) {
+            return stream;
+        }
+    }
+}