clean up runtime code. FELIX-2328

I have removed some dead code and moved all 'optional' commands into the .osgi sub-package. They are still currently added by the Activator, but now must easier to omit.



git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@941833 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/gogo/commands/src/test/java/org/apache/felix/gogo/commands/Context.java b/gogo/commands/src/test/java/org/apache/felix/gogo/commands/Context.java
index 1e266da..9b1fbae 100644
--- a/gogo/commands/src/test/java/org/apache/felix/gogo/commands/Context.java
+++ b/gogo/commands/src/test/java/org/apache/felix/gogo/commands/Context.java
@@ -19,10 +19,10 @@
 package org.apache.felix.gogo.commands;
 
 import org.apache.felix.gogo.runtime.threadio.ThreadIOImpl;
-import org.apache.felix.gogo.runtime.shell.CommandShellImpl;
+import org.apache.felix.gogo.runtime.shell.CommandProcessorImpl;
 import org.apache.felix.gogo.runtime.shell.CommandSessionImpl;
 
-public class Context extends CommandShellImpl
+public class Context extends CommandProcessorImpl
 {
     public static final String EMPTY = "";
     CommandSessionImpl session = (CommandSessionImpl) createSession(System.in, System.out, System.err);
@@ -37,7 +37,7 @@
 
     public Context()
     {
-        setThreadio(threadio);
+        super(threadio);
     }
 
     public Object execute(CharSequence source) throws Exception
diff --git a/gogo/launcher/src/main/java/org/apache/felix/gogo/launcher/Launcher.java b/gogo/launcher/src/main/java/org/apache/felix/gogo/launcher/Launcher.java
index dac6a94..123e0b9 100644
--- a/gogo/launcher/src/main/java/org/apache/felix/gogo/launcher/Launcher.java
+++ b/gogo/launcher/src/main/java/org/apache/felix/gogo/launcher/Launcher.java
@@ -18,7 +18,7 @@
  */
 package org.apache.felix.gogo.launcher;
 
-import org.apache.felix.gogo.runtime.osgi.OSGiShell;
+import org.apache.felix.gogo.runtime.shell.CommandProcessorImpl;
 import org.apache.felix.gogo.runtime.threadio.ThreadIOImpl;
 import org.apache.felix.gogo.console.stdio.Console;
 import org.osgi.framework.Bundle;
@@ -109,10 +109,7 @@
         ThreadIOImpl threadio = new ThreadIOImpl();
         threadio.start();
 
-        OSGiShell shell = new OSGiShell();
-        shell.setThreadio(threadio);
-        shell.setBundle(framework);
-        shell.start();
+        CommandProcessorImpl shell = new CommandProcessorImpl(threadio);
 
         CommandSession session = shell.createSession(in, out, System.err);
         session.put("shell", shell);
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Activator.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Activator.java
index be9b12e..7518b8f 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Activator.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Activator.java
@@ -18,12 +18,15 @@
  */
 package org.apache.felix.gogo.runtime;
 
-import org.apache.felix.gogo.runtime.lang.Support;
-import org.apache.felix.gogo.runtime.osgi.OSGiShell;
+import org.apache.felix.gogo.runtime.osgi.OSGiCommands;
+import org.apache.felix.gogo.runtime.osgi.OSGiConverters;
 import org.apache.felix.gogo.runtime.threadio.ThreadIOImpl;
 import org.apache.felix.gogo.runtime.shell.CommandProxy;
+import org.apache.felix.gogo.runtime.shell.CommandProcessorImpl;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.command.CommandProcessor;
@@ -33,65 +36,96 @@
 import org.osgi.util.tracker.ServiceTracker;
 
 import java.util.HashMap;
-import java.util.Hashtable;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
 public class Activator implements BundleActivator
 {
-
-    private OSGiShell shell;
+    private CommandProcessorImpl processor;
     private ThreadIOImpl threadio;
-    private ServiceRegistration shellRegistration;
-    private ServiceRegistration threadioRegistration;
-    private ServiceTracker converterTracker;
     private ServiceTracker commandTracker;
+    private ServiceTracker converterTracker;
     private ServiceTracker felixTracker;
-    private Map<ServiceReference, ServiceRegistration> regs = new HashMap<ServiceReference, ServiceRegistration>();
+    private ServiceRegistration processorRegistration;
+    private ServiceRegistration threadioRegistration;
+    private Map<ServiceReference, ServiceRegistration> felixRegistrations;
+    private OSGiCommands commands;
+    private OSGiConverters converters;
+    private ServiceRegistration convertersRegistration;
 
     public void start(final BundleContext context) throws Exception
     {
-        Hashtable props = new Hashtable();
-        props.put("osgi.command.scope", "log");
-        props.put("osgi.command.function", "display");
-
         threadio = new ThreadIOImpl();
         threadio.start();
-        shell = new OSGiShell();
-        shell.setBundle(context.getBundle());
-        shell.setThreadio(threadio);
-        shell.setConverter(new Support());
-        shell.start();
+        threadioRegistration = context.registerService(ThreadIO.class.getName(),
+            threadio, null);
+
+        processor = new CommandProcessorImpl(threadio);
+        processorRegistration = context.registerService(CommandProcessor.class.getName(),
+            processor, null);
+        
+        commandTracker = trackOSGiCommands(context);
+        commandTracker.open();
+
+        felixRegistrations = new HashMap<ServiceReference, ServiceRegistration>();
+        felixTracker = trackFelixCommands(context);
+        felixTracker.open();
+
         converterTracker = new ServiceTracker(context, Converter.class.getName(), null)
         {
             @Override
             public Object addingService(ServiceReference reference)
             {
                 Converter converter = (Converter) super.addingService(reference);
-                shell.setConverter(converter);
+                processor.addConverter(converter);
                 return converter;
             }
 
             @Override
             public void removedService(ServiceReference reference, Object service)
             {
-                shell.unsetConverter((Converter) service);
+                processor.removeConverter((Converter) service);
                 super.removedService(reference, service);
             }
         };
         converterTracker.open();
 
-        commandTracker = new ServiceTracker(context,
-            context.createFilter("(&(osgi.command.scope=*)(osgi.command.function=*))"),
-            null)
+        // FIXME: optional?
+        commands = new OSGiCommands(context);
+        commands.registerCommands(processor, context.getBundle());
+        converters = new OSGiConverters(context);
+        convertersRegistration = context.registerService(Converter.class.getCanonicalName(), converters, null);
+    }
+
+    public void stop(BundleContext context) throws Exception
+    {
+        convertersRegistration.unregister();
+        processorRegistration.unregister();
+        threadioRegistration.unregister();
+        
+        commandTracker.close();
+        converterTracker.close();
+        felixTracker.close();
+
+        threadio.stop();
+    }
+
+    private ServiceTracker trackOSGiCommands(final BundleContext context)
+        throws InvalidSyntaxException
+    {
+        Filter filter = context.createFilter(String.format("(&(%s=*)(%s=*))",
+            CommandProcessor.COMMAND_SCOPE, CommandProcessor.COMMAND_FUNCTION));
+
+        return new ServiceTracker(context, filter, null)
         {
             @Override
             public Object addingService(ServiceReference reference)
             {
-                Object scope = reference.getProperty("osgi.command.scope");
-                Object function = reference.getProperty("osgi.command.function");
+                Object scope = reference.getProperty(CommandProcessor.COMMAND_SCOPE);
+                Object function = reference.getProperty(CommandProcessor.COMMAND_FUNCTION);
                 List<Object> commands = new ArrayList<Object>();
+
                 if (scope != null && function != null)
                 {
                     if (function.getClass().isArray())
@@ -100,7 +134,7 @@
                         {
                             Function target = new CommandProxy(context, reference,
                                 f.toString());
-                            shell.addCommand(scope.toString(), target, f.toString());
+                            processor.addCommand(scope.toString(), target, f.toString());
                             commands.add(target);
                         }
                     }
@@ -108,7 +142,7 @@
                     {
                         Function target = new CommandProxy(context, reference,
                             function.toString());
-                        shell.addCommand(scope.toString(), target, function.toString());
+                        processor.addCommand(scope.toString(), target, function.toString());
                         commands.add(target);
                     }
                     return commands;
@@ -119,18 +153,32 @@
             @Override
             public void removedService(ServiceReference reference, Object service)
             {
-                List<Object> commands = (List<Object>) service;
-                for (Object cmd : commands)
+                Object scope = reference.getProperty(CommandProcessor.COMMAND_SCOPE);
+                Object function = reference.getProperty(CommandProcessor.COMMAND_FUNCTION);
+
+                if (scope != null && function != null)
                 {
-                    shell.removeCommand(cmd);
+                    if (!function.getClass().isArray())
+                    {
+                        processor.removeCommand(scope.toString(), function.toString());
+                    }
+                    else
+                    {
+                        for (Object func : (Object[]) function)
+                        {
+                            processor.removeCommand(scope.toString(), func.toString());
+                        }
+                    }
                 }
+
                 super.removedService(reference, service);
             }
         };
-        commandTracker.open();
+    }
 
-        felixTracker = new ServiceTracker(context, FelixCommandAdaptor.FELIX_COMMAND,
-            null)
+    private ServiceTracker trackFelixCommands(final BundleContext context)
+    {
+        return new ServiceTracker(context, FelixCommandAdaptor.FELIX_COMMAND, null)
         {
             @Override
             public Object addingService(ServiceReference ref)
@@ -139,7 +187,7 @@
                 try
                 {
                     FelixCommandAdaptor adaptor = new FelixCommandAdaptor(felixCommand);
-                    regs.put(ref, context.registerService(
+                    felixRegistrations.put(ref, context.registerService(
                         FelixCommandAdaptor.class.getName(), adaptor,
                         adaptor.getAttributes()));
                     return felixCommand;
@@ -154,37 +202,13 @@
             @Override
             public void removedService(ServiceReference reference, Object service)
             {
-                ServiceRegistration reg = regs.remove(reference);
+                ServiceRegistration reg = felixRegistrations.remove(reference);
                 if (reg != null)
+                {
                     reg.unregister();
+                }
                 super.removedService(reference, service);
             }
         };
-        felixTracker.open();
-
-        threadioRegistration = context.registerService(ThreadIO.class.getName(),
-            threadio, new Hashtable());
-        shellRegistration = context.registerService(CommandProcessor.class.getName(),
-            shell, new Hashtable());
-    }
-
-    private String getProperty(BundleContext context, String name, String def)
-    {
-        String v = context.getProperty(name);
-        if (v == null)
-        {
-            v = def;
-        }
-        return v;
-    }
-
-    public void stop(BundleContext context) throws Exception
-    {
-        shellRegistration.unregister();
-        threadioRegistration.unregister();
-        threadio.stop();
-        converterTracker.close();
-        commandTracker.close();
-        felixTracker.close();
     }
 }
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/FelixCommandAdaptor.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/FelixCommandAdaptor.java
index b9e8ff3..1aeefc3 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/FelixCommandAdaptor.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/FelixCommandAdaptor.java
@@ -24,14 +24,15 @@
         Class<?>[] parms = { String.class, PrintStream.class, PrintStream.class };
         execute = c.getMethod("execute", parms);
 
-        Method name = c.getMethod("getName", (Class[]) null);
-        this.name = (String) name.invoke(felixCommand, (Object[]) null);
+        Method m;
+        m = c.getMethod("getName", (Class[]) null);
+        name = (String) m.invoke(felixCommand, (Object[]) null);
 
-        Method help = c.getMethod("getShortDescription", (Class[]) null);
-        this.help = (String) help.invoke(felixCommand, (Object[]) null);
+        m = c.getMethod("getShortDescription", (Class[]) null);
+        help = (String) m.invoke(felixCommand, (Object[]) null);
 
-        Method usage = c.getMethod("getUsage", (Class[]) null);
-        this.usage = (String) usage.invoke(felixCommand, (Object[]) null);
+        m = c.getMethod("getUsage", (Class[]) null);
+        usage = (String) m.invoke(felixCommand, (Object[]) null);
     }
 
     public void _main(String[] argv) throws Exception
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/lang/Support.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/lang/Support.java
deleted file mode 100644
index 7a19eef..0000000
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/lang/Support.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.felix.gogo.runtime.lang;
-
-import org.osgi.service.command.Converter;
-import org.osgi.service.command.Function;
-
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.util.Arrays;
-
-public class Support implements Converter
-{
-
-    public Object convert(Class<?> desiredType, final Object in) throws Exception
-    {
-        if (in instanceof Function && desiredType.isInterface()
-            && desiredType.getDeclaredMethods().length == 1)
-        {
-            return Proxy.newProxyInstance(desiredType.getClassLoader(),
-                new Class[] { desiredType }, new InvocationHandler()
-                {
-                    Function command = ((Function) in);
-
-                    public Object invoke(Object proxy, Method method, Object[] args)
-                        throws Throwable
-                    {
-                        return command.execute(null, Arrays.asList(args));
-                    }
-
-                });
-        }
-        return null;
-    }
-
-    public CharSequence format(Object target, int level, Converter escape)
-        throws Exception
-    {
-        return null;
-    }
-}
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/OSGiCommands.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/OSGiCommands.java
index 9ca2be7..e8e08f5 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/OSGiCommands.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/OSGiCommands.java
@@ -16,152 +16,98 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-// DWB1: osgi:each too verbose (formats reults to System.out)
-// DWB2: ClassNotFoundException should be caught in convert() method
 package org.apache.felix.gogo.runtime.osgi;
 
-import org.osgi.framework.*;
-import org.osgi.service.command.CommandSession;
-import org.osgi.service.command.Converter;
-import org.osgi.service.command.Function;
-
-import java.io.*;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Formatter;
-import java.util.List;
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
 import java.util.regex.Pattern;
 
-public class OSGiCommands implements Converter
+import org.apache.felix.gogo.runtime.shell.CommandProcessorImpl;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.command.CommandSession;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+public class OSGiCommands
 {
-    Bundle bundle;
-    String COLUMN = "%40s %s\n";
+    final BundleContext context;
 
-    protected OSGiCommands(Bundle bundle)
+    public OSGiCommands(BundleContext context)
     {
-        this.bundle = bundle;
+        this.context = context;
     }
 
-    //	Bundle[] getBundles() {
-    //		return getContext().getBundles();
-    //	}
-
-    public BundleContext getContext()
+    private Object service(String clazz, String filter) throws InvalidSyntaxException
     {
-        if (bundle.getState() != Bundle.ACTIVE && bundle.getState() != Bundle.STARTING
-            && bundle.getState() != Bundle.STOPPING)
+        ServiceReference ref[] = context.getServiceReferences(clazz, filter);
+        if (ref != null)
         {
-            throw new IllegalStateException("Framework is not started yet");
-        }
-        return bundle.getBundleContext();
-    }
-
-    CharSequence print(Bundle bundle)
-    {
-        String version = (String) bundle.getHeaders().get("Bundle-Version");
-        if (version == null)
-        {
-            version = "0.0.0";
-        }
-        return String.format("%06d %s %s-%s", bundle.getBundleId(), getState(bundle),
-            bundle.getSymbolicName(), version);
-    }
-
-    CharSequence print(ServiceReference ref)
-    {
-        StringBuilder sb = new StringBuilder();
-        Formatter f = new Formatter(sb);
-
-        String spid = "";
-        Object pid = ref.getProperty("service.pid");
-        if (pid != null)
-        {
-            spid = pid.toString();
+            return context.getService(ref[0]);
         }
 
-        f.format("%06d %3s %-40s %s", ref.getProperty("service.id"),
-            ref.getBundle().getBundleId(),
-            getShortNames((String[]) ref.getProperty("objectclass")), spid);
-        return sb;
-    }
-
-    CharSequence getShortNames(String[] list)
-    {
-        StringBuilder sb = new StringBuilder();
-        String del = "";
-        for (String s : list)
-        {
-            sb.append(del + getShortName(s));
-            del = " | ";
-        }
-        return sb;
-    }
-
-    CharSequence getShortName(String name)
-    {
-        int n = name.lastIndexOf('.');
-        if (n < 0)
-        {
-            n = 0;
-        }
-        else
-        {
-            n++;
-        }
-        return name.subSequence(n, name.length());
-    }
-
-    private String getState(Bundle bundle)
-    {
-        switch (bundle.getState())
-        {
-            case Bundle.ACTIVE:
-                return "ACT";
-
-            case Bundle.INSTALLED:
-                return "INS";
-
-            case Bundle.RESOLVED:
-                return "RES";
-
-            case Bundle.STARTING:
-                return "STA";
-
-            case Bundle.STOPPING:
-                return "STO";
-
-            case Bundle.UNINSTALLED:
-                return "UNI ";
-        }
         return null;
     }
 
-    public void grep(String match) throws IOException
+    public void registerCommands(CommandProcessorImpl processor, Bundle bundle)
     {
-        Pattern p = Pattern.compile(match);
-        BufferedReader rdr = new BufferedReader(new InputStreamReader(System.in));
-        String s = rdr.readLine();
-        while (s != null)
+        processor.addCommand("osgi", this);
+        processor.addCommand("osgi", new Procedural());
+        processor.addCommand("osgi", bundle);
+        processor.addCommand("osgi", context, BundleContext.class);
+
+        try
         {
-            if (p.matcher(s).find())
+            processor.addCommand("osgi",
+                this.service(PackageAdmin.class.getName(), null), PackageAdmin.class);
+
+            try
             {
-                System.out.println(s);
+                // dynamically load StartLevel to avoid import dependency
+                String sl = "org.osgi.service.startlevel.StartLevel";
+                Class<?> slClass = bundle.loadClass(sl);
+                processor.addCommand("osgi", this.service(sl, null), slClass);
             }
-            s = rdr.readLine();
+            catch (ClassNotFoundException e)
+            {
+            }
+
+            try
+            {
+                // dynamically load PermissionAdmin to avoid import dependency
+                String pa = "org.osgi.service.permissionadmin.PermissionAdmin";
+                Class<?> paClass = bundle.loadClass(pa);
+                processor.addCommand("osgi", this.service(pa, null), paClass);
+            }
+            catch (ClassNotFoundException e)
+            {
+            }
+        }
+        catch (InvalidSyntaxException e)
+        {
+            // can't happen with null filter
         }
     }
 
-    public String tac() throws IOException
+    public Bundle bundle(Bundle i)
     {
-        StringWriter sw = new StringWriter();
-        BufferedReader rdr = new BufferedReader(new InputStreamReader(System.in));
-        String s = rdr.readLine();
-        while (s != null)
-        {
-            sw.write(s);
-            s = rdr.readLine();
-        }
-        return sw.toString();
+        return i;
+    }
+
+    public void start(Bundle b) throws BundleException
+    {
+        b.start();
+    }
+
+    public void stop(Bundle b) throws BundleException
+    {
+        b.stop();
     }
 
     public CharSequence echo(CommandSession session, Object args[])
@@ -180,59 +126,6 @@
         return sb;
     }
 
-    public void each(CommandSession session, Collection<Object> list, Function closure)
-        throws Exception
-    {
-        List<Object> args = new ArrayList<Object>();
-        args.add(null);
-        for (Object x : list)
-        {
-            args.set(0, x);
-            //Object result = closure.execute(session, args);
-            // System.out.println(session.format(result,Converter.INSPECT));
-            // derek: this is way too noisy
-            closure.execute(session, args);
-        }
-    }
-
-    public Bundle bundle(Bundle i)
-    {
-        return i;
-    }
-/*
-    public String[] ls(CommandSession session, File f) throws Exception
-    {
-        File cwd = (File) session.get("_cwd");
-        if (cwd == null)
-        {
-            cwd = new File("").getAbsoluteFile();
-        }
-
-        if (f == null)
-        {
-            f = cwd;
-        }
-        else
-        {
-            if (!f.isAbsolute())
-            {
-                f = new File(cwd, f.getPath());
-            }
-        }
-
-        if (f.isDirectory())
-        {
-            return f.list();
-        }
-
-        if (f.isFile())
-        {
-            cat(session, f);
-        }
-
-        return null;
-    }
-*/
     public Object cat(CommandSession session, File f) throws Exception
     {
         File cwd = (File) session.get("_cwd");
@@ -258,167 +151,19 @@
         return new String(bout.toByteArray());
     }
 
-    public Object convert(Class<?> desiredType, Object in) throws Exception
+    public void grep(String match) throws IOException
     {
-        if (desiredType == Bundle.class)
+        Pattern p = Pattern.compile(match);
+        BufferedReader rdr = new BufferedReader(new InputStreamReader(System.in));
+        String s = rdr.readLine();
+        while (s != null)
         {
-            return convertBundle(in);
-        }
-        else
-        {
-            if (desiredType == ServiceReference.class)
+            if (p.matcher(s).find())
             {
-                return convertServiceReference(in);
+                System.out.println(s);
             }
-            else
-            {
-                if (desiredType == Class.class)
-                {
-                    // derek.baum@paremus.com - added try/catch
-                    try
-                    {
-                        return Class.forName(in.toString());
-                    }
-                    catch (ClassNotFoundException e)
-                    {
-                        return null;
-                    }
-                }
-                else
-                {
-                    if (desiredType.isAssignableFrom(String.class)
-                        && in instanceof InputStream)
-                    {
-                        return read(((InputStream) in));
-                    }
-                }
-            }
+            s = rdr.readLine();
         }
-
-        return null;
-    }
-
-    private Object convertServiceReference(Object in) throws InvalidSyntaxException
-    {
-        String s = in.toString();
-        if (s.startsWith("(") && s.endsWith(")"))
-        {
-            ServiceReference refs[] = getContext().getServiceReferences(null,
-                String.format("(|(service.id=%s)(service.pid=%s))", in, in));
-            if (refs != null && refs.length > 0)
-            {
-                return refs[0];
-            }
-        }
-
-        ServiceReference refs[] = getContext().getServiceReferences(null,
-            String.format("(|(service.id=%s)(service.pid=%s))", in, in));
-        if (refs != null && refs.length > 0)
-        {
-            return refs[0];
-        }
-        return null;
-    }
-
-    private Object convertBundle(Object in)
-    {
-        String s = in.toString();
-        try
-        {
-            long id = Long.parseLong(s);
-            return getContext().getBundle(id);
-        }
-        catch (NumberFormatException nfe)
-        {
-            // Ignore
-        }
-
-        Bundle bundles[] = getContext().getBundles();
-        for (Bundle b : bundles)
-        {
-            if (b.getLocation().equals(s))
-            {
-                return b;
-            }
-
-            if (b.getSymbolicName().equals(s))
-            {
-                return b;
-            }
-        }
-
-        return null;
-    }
-
-    public CharSequence format(Object target, int level, Converter converter)
-        throws IOException
-    {
-        if (level == INSPECT && target instanceof InputStream)
-        {
-            return read(((InputStream) target));
-        }
-        if (level == LINE && target instanceof Bundle)
-        {
-            return print((Bundle) target);
-        }
-        if (level == LINE && target instanceof ServiceReference)
-        {
-            return print((ServiceReference) target);
-        }
-        if (level == PART && target instanceof Bundle)
-        {
-            return ((Bundle) target).getSymbolicName();
-        }
-        if (level == PART && target instanceof ServiceReference)
-        {
-            return getShortNames((String[]) ((ServiceReference) target).getProperty("objectclass"));
-        }
-        return null;
-    }
-
-    public CharSequence read(InputStream in) throws IOException
-    {
-        int c;
-        StringBuffer sb = new StringBuffer();
-        while ((c = in.read()) > 0)
-        {
-            if (c >= 32 && c <= 0x7F || c == '\n' || c == '\r')
-            {
-                sb.append((char) c);
-            }
-            else
-            {
-                String s = Integer.toHexString(c).toUpperCase();
-                sb.append("\\");
-                if (s.length() < 1)
-                {
-                    sb.append(0);
-                }
-                sb.append(s);
-            }
-        }
-        return sb;
-    }
-
-    public void start(Bundle b) throws BundleException
-    {
-        b.start();
-    }
-
-    public void stop(Bundle b) throws BundleException
-    {
-        b.stop();
-    }
-
-    public Object service(String clazz, String filter) throws InvalidSyntaxException
-    {
-        ServiceReference ref[] = getContext().getServiceReferences(clazz, filter);
-        if (ref == null)
-        {
-            return null;
-        }
-
-        return getContext().getService(ref[0]);
     }
 
 }
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/OSGiConverters.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/OSGiConverters.java
new file mode 100644
index 0000000..5d6e686
--- /dev/null
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/OSGiConverters.java
@@ -0,0 +1,279 @@
+/*
+ * 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.gogo.runtime.osgi;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.Formatter;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.command.Converter;
+import org.osgi.service.command.Function;
+
+public class OSGiConverters implements Converter
+{
+    private final BundleContext context;
+    public OSGiConverters(BundleContext context)
+    {
+        this.context = context;
+    }
+
+    private CharSequence print(Bundle bundle)
+    {
+        String version = (String) bundle.getHeaders().get("Bundle-Version");
+        if (version == null)
+        {
+            version = "0.0.0";
+        }
+        return String.format("%06d %s %s-%s", bundle.getBundleId(), getState(bundle),
+            bundle.getSymbolicName(), version);
+    }
+
+    private CharSequence print(ServiceReference ref)
+    {
+        StringBuilder sb = new StringBuilder();
+        Formatter f = new Formatter(sb);
+
+        String spid = "";
+        Object pid = ref.getProperty("service.pid");
+        if (pid != null)
+        {
+            spid = pid.toString();
+        }
+
+        f.format("%06d %3s %-40s %s", ref.getProperty("service.id"),
+            ref.getBundle().getBundleId(),
+            getShortNames((String[]) ref.getProperty("objectclass")), spid);
+        return sb;
+    }
+
+    private CharSequence getShortNames(String[] list)
+    {
+        StringBuilder sb = new StringBuilder();
+        String del = "";
+        for (String s : list)
+        {
+            sb.append(del + getShortName(s));
+            del = " | ";
+        }
+        return sb;
+    }
+
+    private CharSequence getShortName(String name)
+    {
+        int n = name.lastIndexOf('.');
+        if (n < 0)
+        {
+            n = 0;
+        }
+        else
+        {
+            n++;
+        }
+        return name.subSequence(n, name.length());
+    }
+
+    private String getState(Bundle bundle)
+    {
+        switch (bundle.getState())
+        {
+            case Bundle.ACTIVE:
+                return "ACT";
+
+            case Bundle.INSTALLED:
+                return "INS";
+
+            case Bundle.RESOLVED:
+                return "RES";
+
+            case Bundle.STARTING:
+                return "STA";
+
+            case Bundle.STOPPING:
+                return "STO";
+
+            case Bundle.UNINSTALLED:
+                return "UNI ";
+        }
+        return null;
+    }
+
+    public Bundle bundle(Bundle i)
+    {
+        return i;
+    }
+
+    public Object convert(Class<?> desiredType, final Object in) throws Exception
+    {
+        if (desiredType == Bundle.class)
+        {
+            return convertBundle(in);
+        }
+
+        if (desiredType == ServiceReference.class)
+        {
+            return convertServiceReference(in);
+        }
+
+        if (desiredType == Class.class)
+        {
+            try
+            {
+                return Class.forName(in.toString());
+            }
+            catch (ClassNotFoundException e)
+            {
+                return null;
+            }
+        }
+
+        if (desiredType.isAssignableFrom(String.class) && in instanceof InputStream)
+        {
+            return read(((InputStream) in));
+        }
+
+        if (in instanceof Function && desiredType.isInterface()
+            && desiredType.getDeclaredMethods().length == 1)
+        {
+            return Proxy.newProxyInstance(desiredType.getClassLoader(),
+                new Class[] { desiredType }, new InvocationHandler()
+                {
+                    Function command = ((Function) in);
+
+                    public Object invoke(Object proxy, Method method, Object[] args)
+                        throws Throwable
+                    {
+                        return command.execute(null, Arrays.asList(args));
+                    }
+                });
+        }
+
+        return null;
+    }
+
+    private Object convertServiceReference(Object in) throws InvalidSyntaxException
+    {
+        String s = in.toString();
+        if (s.startsWith("(") && s.endsWith(")"))
+        {
+            ServiceReference refs[] = context.getServiceReferences(null, String.format(
+                "(|(service.id=%s)(service.pid=%s))", in, in));
+            if (refs != null && refs.length > 0)
+            {
+                return refs[0];
+            }
+        }
+
+        ServiceReference refs[] = context.getServiceReferences(null, String.format(
+            "(|(service.id=%s)(service.pid=%s))", in, in));
+        if (refs != null && refs.length > 0)
+        {
+            return refs[0];
+        }
+        return null;
+    }
+
+    private Object convertBundle(Object in)
+    {
+        String s = in.toString();
+        try
+        {
+            long id = Long.parseLong(s);
+            return context.getBundle(id);
+        }
+        catch (NumberFormatException nfe)
+        {
+            // Ignore
+        }
+
+        Bundle bundles[] = context.getBundles();
+        for (Bundle b : bundles)
+        {
+            if (b.getLocation().equals(s))
+            {
+                return b;
+            }
+
+            if (b.getSymbolicName().equals(s))
+            {
+                return b;
+            }
+        }
+
+        return null;
+    }
+
+    public CharSequence format(Object target, int level, Converter converter)
+        throws IOException
+    {
+        if (level == INSPECT && target instanceof InputStream)
+        {
+            return read(((InputStream) target));
+        }
+        if (level == LINE && target instanceof Bundle)
+        {
+            return print((Bundle) target);
+        }
+        if (level == LINE && target instanceof ServiceReference)
+        {
+            return print((ServiceReference) target);
+        }
+        if (level == PART && target instanceof Bundle)
+        {
+            return ((Bundle) target).getSymbolicName();
+        }
+        if (level == PART && target instanceof ServiceReference)
+        {
+            return getShortNames((String[]) ((ServiceReference) target).getProperty("objectclass"));
+        }
+        return null;
+    }
+
+    private CharSequence read(InputStream in) throws IOException
+    {
+        int c;
+        StringBuffer sb = new StringBuffer();
+        while ((c = in.read()) > 0)
+        {
+            if (c >= 32 && c <= 0x7F || c == '\n' || c == '\r')
+            {
+                sb.append((char) c);
+            }
+            else
+            {
+                String s = Integer.toHexString(c).toUpperCase();
+                sb.append("\\");
+                if (s.length() < 1)
+                {
+                    sb.append(0);
+                }
+                sb.append(s);
+            }
+        }
+        return sb;
+    }
+
+}
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/OSGiShell.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/OSGiShell.java
deleted file mode 100644
index 17556b1..0000000
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/OSGiShell.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-// DWB3: dynamically load optional framework components to reduce dependencies
-// DWB4: get() with trailing colon causes org.osgi.framework.InvalidSyntaxException
-package org.apache.felix.gogo.runtime.osgi;
-
-import org.apache.felix.gogo.runtime.shell.CommandShellImpl;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.command.Converter;
-import org.osgi.service.packageadmin.PackageAdmin;
-import org.osgi.service.threadio.ThreadIO;
-
-public class OSGiShell extends CommandShellImpl
-{
-    Bundle bundle;
-    OSGiCommands commands;
-
-    public void start() throws Exception
-    {
-        commands = new OSGiCommands(bundle);
-        addCommand("osgi", this.bundle);
-        addCommand("osgi", commands);
-        setConverter(commands);
-        if (bundle.getState() == Bundle.ACTIVE || bundle.getState() == Bundle.STARTING)
-        {
-            addCommand("osgi", commands.service(PackageAdmin.class.getName(), null),
-                PackageAdmin.class);
-            addCommand("osgi", commands.getContext(), BundleContext.class);
-
-            try
-            {
-                // derek - dynamically load StartLevel to avoid import dependency
-                String sl = "org.osgi.service.startlevel.StartLevel";
-                Class<?> slClass = bundle.loadClass(sl);
-                addCommand("osgi", commands.service(sl, null), slClass);
-            }
-            catch (ClassNotFoundException e)
-            {
-            }
-
-            try
-            {
-                // derek - dynamically load PermissionAdmin to avoid import dependency
-                String pa = "org.osgi.service.permissionadmin.PermissionAdmin";
-                Class<?> paClass = bundle.loadClass(pa);
-                addCommand("osgi", commands.service(pa, null), paClass);
-            }
-            catch (ClassNotFoundException e)
-            {
-            }
-        }
-        else
-        {
-            System.err.println("eek! bundle not active: " + bundle);
-        }
-    }
-
-    public Object get(String name)
-    {
-        if (bundle.getBundleContext() != null)
-        {
-            BundleContext context = bundle.getBundleContext();
-            try
-            {
-                Object cmd = super.get(name);
-                if (cmd != null)
-                {
-                    return cmd;
-                }
-
-                int n = name.indexOf(':');
-                if (n < 0)
-                {
-                    return null;
-                }
-
-                String service = name.substring(0, n);
-                String function = name.substring(n + 1);
-
-                // derek - fix org.osgi.framework.InvalidSyntaxException
-                if (service.length() == 0 || function.length() == 0)
-                {
-                    return null;
-                }
-
-                String filter = String.format(
-                    "(&(osgi.command.scope=%s)(osgi.command.function=%s))", service,
-                    function);
-                ServiceReference refs[] = context.getServiceReferences(null, filter);
-                if (refs == null || refs.length == 0)
-                {
-                    return null;
-                }
-
-                if (refs.length > 1)
-                {
-                    throw new IllegalArgumentException(
-                        "Command name is not unambiguous: " + name
-                            + ", found multiple impls");
-                }
-
-                return new ServiceCommand(this, refs[0], function);
-            }
-            catch (InvalidSyntaxException ise)
-            {
-                ise.printStackTrace();
-            }
-        }
-        return super.get(name);
-    }
-
-    public void setThreadio(Object t)
-    {
-        super.setThreadio((ThreadIO) t);
-    }
-
-    public void setBundle(Bundle bundle)
-    {
-        this.bundle = bundle;
-    }
-
-    public void setConverter(Converter c)
-    {
-        super.setConverter(c);
-    }
-
-}
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/cpeg/Procedural.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/Procedural.java
similarity index 69%
rename from gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/cpeg/Procedural.java
rename to gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/Procedural.java
index e297c08..2ef5325 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/cpeg/Procedural.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/Procedural.java
@@ -16,7 +16,15 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.felix.gogo.runtime.cpeg;
+package org.apache.felix.gogo.runtime.osgi;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 
 import org.osgi.framework.Bundle;
 import org.osgi.service.command.CommandSession;
@@ -24,6 +32,30 @@
 
 public class Procedural
 {
+    public String tac() throws IOException
+    {
+        StringWriter sw = new StringWriter();
+        BufferedReader rdr = new BufferedReader(new InputStreamReader(System.in));
+        String s = rdr.readLine();
+        while (s != null)
+        {
+            sw.write(s);
+            s = rdr.readLine();
+        }
+        return sw.toString();
+    }
+    
+    public void each(CommandSession session, Collection<Object> list, Function closure)
+        throws Exception
+    {
+        List<Object> args = new ArrayList<Object>();
+        args.add(null);
+        for (Object x : list)
+        {
+            args.set(0, x);
+            closure.execute(session, args);
+        }
+    }
 
     public Object _if(CommandSession session, Function condition, Function ifTrue,
         Function ifFalse) throws Exception
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/ServiceCommand.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/ServiceCommand.java
deleted file mode 100644
index 0e8a617..0000000
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/ServiceCommand.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.felix.gogo.runtime.osgi;
-
-import org.apache.felix.gogo.runtime.shell.CommandShellImpl;
-import org.apache.felix.gogo.runtime.shell.Reflective;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.command.CommandSession;
-import org.osgi.service.command.Function;
-
-import java.util.List;
-
-public class ServiceCommand extends Reflective implements Function
-{
-    ServiceReference ref;
-    OSGiShell shell;
-    String name;
-
-    public ServiceCommand(OSGiShell shell, ServiceReference ref, String name)
-    {
-        this.shell = shell;
-        this.ref = ref;
-        this.name = name;
-    }
-
-    public Object execute(CommandSession session, List<Object> arguments)
-        throws Exception
-    {
-        try
-        {
-            Object target = shell.bundle.getBundleContext().getService(ref);
-            if (target instanceof Function)
-            {
-                return ((Function) target).execute(session, arguments);
-            }
-            else
-            {
-                Object result = method(session, target, name, arguments);
-                if (result != CommandShellImpl.NO_SUCH_COMMAND)
-                {
-                    return result;
-                }
-                throw new IllegalArgumentException(
-                    "Service does not implement promised command " + ref + " " + name);
-            }
-        }
-        finally
-        {
-            shell.bundle.getBundleContext().ungetService(ref);
-        }
-    }
-}
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Command.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Command.java
deleted file mode 100644
index 2488697..0000000
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Command.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.felix.gogo.runtime.shell;
-
-import org.osgi.service.command.CommandSession;
-import org.osgi.service.command.Function;
-import org.osgi.framework.ServiceReference;
-
-import java.util.List;
-
-public class Command extends Reflective implements Function
-{
-    Object target;
-    String function;
-
-    public Command(Object target, String function)
-    {
-        this.function = function;
-        this.target = target;
-    }
-
-    public Object execute(CommandSession session, List<Object> arguments)
-        throws Exception
-    {
-        return method(session, target, function, arguments);
-    }
-}
\ No newline at end of file
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/CommandShellImpl.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/CommandProcessorImpl.java
similarity index 64%
rename from gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/CommandShellImpl.java
rename to gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/CommandProcessorImpl.java
index aa6f0c0..c217e73 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/CommandShellImpl.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/CommandProcessorImpl.java
@@ -16,33 +16,35 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-// DWB11: add removeCommand: https://www.osgi.org/bugzilla/show_bug.cgi?id=49
-// DWB12: there is no API to list commands: https://www.osgi.org/bugzilla/show_bug.cgi?id=49
-// DWB13: addCommand() fails to add static methods (if target is Class)
 package org.apache.felix.gogo.runtime.shell;
 
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
 import org.osgi.service.command.CommandProcessor;
 import org.osgi.service.command.CommandSession;
 import org.osgi.service.command.Converter;
 import org.osgi.service.command.Function;
 import org.osgi.service.threadio.ThreadIO;
 
-import java.io.InputStream;
-import java.io.PrintStream;
-import java.lang.reflect.Method;
-import java.util.*;
-
-public class CommandShellImpl implements CommandProcessor
+public class CommandProcessorImpl implements CommandProcessor
 {
-    Set<Converter> converters = new HashSet<Converter>();
-    protected ThreadIO threadIO;
-    public final static Object NO_SUCH_COMMAND = new Object();
-    protected Map<String, Object> commands = new LinkedHashMap<String, Object>();
+    protected final Set<Converter> converters = new HashSet<Converter>();
+    protected final Map<String, Object> commands = new LinkedHashMap<String, Object>();
+    protected final ThreadIO threadIO;
 
-    public CommandShellImpl()
+    public CommandProcessorImpl(ThreadIO tio)
     {
+        threadIO = tio;
         addCommand("shell", this, "addCommand");
-        addCommand("shell", this, "removeCommand"); // derek
+        addCommand("shell", this, "removeCommand");
     }
 
     public CommandSession createSession(InputStream in, PrintStream out, PrintStream err)
@@ -50,85 +52,59 @@
         return new CommandSessionImpl(this, in, out, err);
     }
 
-    public void setThreadio(ThreadIO threadIO)
-    {
-        this.threadIO = threadIO;
-    }
-
-    public void setConverter(Converter c)
+    public void addConverter(Converter c)
     {
         converters.add(c);
     }
 
-    public void unsetConverter(Converter c)
+    public void removeConverter(Converter c)
     {
         converters.remove(c);
     }
-
-    public Object get(String name)
+    
+    public Map<String, Object> getCommands()
     {
-        if (name == null)
-        {
-            return commands.keySet();
-        }
+        return commands;
+    }
+
+    public Function getCommand(String name)
+    {
         name = name.toLowerCase();
         int n = name.indexOf(':');
+
         if (n < 0)
         {
             return null;
         }
-
-        String function = name.substring(n);
-
-        Object cmd = null;
-
-        if (commands.containsKey(name))
+        
+        String scope = name.substring(0, n);
+        String function = name.substring(n + 1);
+        Object cmd = commands.get(name);
+        
+        if (null == cmd && scope.equals("*"))
         {
-            cmd = commands.get(name);
-        }
-        else
-        {
-            String scope = name.substring(0, n);
-            if (scope.equals("*"))
+            for (String key : commands.keySet())
             {
-                for (Map.Entry<String, Object> entry : commands.entrySet())
+                if (key.endsWith(":" + function))
                 {
-                    if (entry.getKey().endsWith(function))
-                    {
-                        cmd = entry.getValue();
-                        break;
-                    }
+                    cmd = commands.get(key);
+                    break;
                 }
             }
-
-            // XXX: derek.baum@paremus.com
-            // there is no API to list commands
-            // so override otherwise illegal name ":"
-            if (cmd == null && name.equals(":"))
-            {
-                return Collections.unmodifiableSet(commands.keySet());
-            }
         }
 
-        if (cmd == null)
+        if ((null == cmd) || (cmd instanceof Function))
         {
-            return null;
+            return (Function) cmd;
         }
 
-        if (cmd instanceof Function)
-        {
-            return cmd;
-        }
-        else
-        {
-            return new Command(cmd, function.substring(1));
-        }
+        return new CommandProxy(cmd, function);
     }
 
     public void addCommand(String scope, Object target)
     {
-        // derek - fix target class
-        Class<?> tc = (target instanceof Class) ? (Class<?>) target : target.getClass();
+        Class<?> tc = (target instanceof Class<?>) ? (Class<?>) target
+            : target.getClass();
         addCommand(scope, target, tc);
     }
 
@@ -151,7 +127,6 @@
         commands.put((scope + ":" + function).toLowerCase(), target);
     }
 
-    // derek.baum@paremus.com: need removeCommand, so stopped bundles can clean up.
     public void removeCommand(String scope, String function)
     {
         String func = (scope + ":" + function).toLowerCase();
@@ -176,7 +151,7 @@
         Method methods[] = target.getMethods();
         for (Method m : methods)
         {
-            if (m.getDeclaringClass().equals(Object.class)) // derek
+            if (m.getDeclaringClass().equals(Object.class))
             {
                 continue;
             }
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/CommandProxy.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/CommandProxy.java
index 18f8505..a70a1e2 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/CommandProxy.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/CommandProxy.java
@@ -27,9 +27,10 @@
 
 public class CommandProxy extends Reflective implements Function
 {
-    BundleContext context;
-    ServiceReference reference;
-    String function;
+    private BundleContext context;
+    private ServiceReference reference;
+    private String function;
+    private Object target;
 
     public CommandProxy(BundleContext context, ServiceReference reference, String function)
     {
@@ -37,25 +38,35 @@
         this.reference = reference;
         this.function = function;
     }
+    
+    public CommandProxy(Object target, String function)
+    {
+        this.function = function;
+        this.target = target;
+    }
 
     public Object execute(CommandSession session, List<Object> arguments)
         throws Exception
     {
-        Object target = context.getService(reference);
+        Object tgt = (context != null ? context.getService(reference) : target);
+        
         try
         {
-            if (target instanceof Function)
+            if (tgt instanceof Function)
             {
-                return ((Function) target).execute(session, arguments);
+                return ((Function) tgt).execute(session, arguments);
             }
             else
             {
-                return method(session, target, function, arguments);
+                return method(session, tgt, function, arguments);
             }
         }
         finally
         {
-            context.ungetService(reference);
+            if (context != null)
+            {
+                context.ungetService(reference);
+            }
         }
     }
 }
\ No newline at end of file
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/CommandSessionImpl.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/CommandSessionImpl.java
index 1a26221..ce47d0a 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/CommandSessionImpl.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/CommandSessionImpl.java
@@ -23,6 +23,7 @@
 
 import org.osgi.service.command.CommandSession;
 import org.osgi.service.command.Converter;
+import org.osgi.service.threadio.ThreadIO;
 
 import java.io.InputStream;
 import java.io.PrintStream;
@@ -35,20 +36,27 @@
     public static final String VARIABLES = ".variables";
     public static final String COMMANDS = ".commands";
     private static final String COLUMN = "%-20s %s\n";
+    
     protected InputStream in;
     protected PrintStream out;
     PrintStream err;
-    CommandShellImpl service;
+    
+    private final CommandProcessorImpl processor;
     protected final Map<String, Object> variables = new HashMap<String, Object>();
     private boolean closed;
 
-    protected CommandSessionImpl(CommandShellImpl service, InputStream in, PrintStream out, PrintStream err)
+    protected CommandSessionImpl(CommandProcessorImpl shell, InputStream in, PrintStream out, PrintStream err)
     {
-        this.service = service;
+        this.processor = shell;
         this.in = in;
         this.out = out;
         this.err = err;
     }
+    
+    ThreadIO threadIO()
+    {
+        return processor.threadIO;
+    }
 
     public void close()
     {
@@ -57,8 +65,8 @@
 
     public Object execute(CharSequence commandline) throws Exception
     {
-        assert service != null;
-        assert service.threadIO != null;
+        assert processor != null;
+        assert processor.threadIO != null;
 
         if (closed)
         {
@@ -82,9 +90,10 @@
         {
             return variables.keySet();
         }
+        
         if (COMMANDS.equals(name))
         {
-            return service.get(null);
+            return processor.getCommands();
         }
 
         if (variables.containsKey(name))
@@ -95,12 +104,13 @@
         // add SCOPE support
         if (name.startsWith("*:"))
         {
+            String func = name.substring(2);
             String path = variables.containsKey("SCOPE") ? variables.get("SCOPE").toString()
                 : "osgi:*";
-            String func = name.substring(2);
+            
             for (String scope : path.split(":"))
             {
-                Object result = service.get(scope + ":" + func);
+                Object result = processor.getCommand(scope + ":" + func);
                 if (result != null)
                 {
                     return result;
@@ -108,7 +118,8 @@
             }
             return null;
         }
-        return service.get(name);
+
+        return processor.getCommand(name);
     }
 
     public void put(String name, Object value)
@@ -135,7 +146,7 @@
             return (CharSequence) target;
         }
 
-        for (Converter c : service.converters)
+        for (Converter c : processor.converters)
         {
             CharSequence s = c.format(target, level, this);
             if (s != null)
@@ -338,7 +349,7 @@
 
     public Object convert(Class<?> desiredType, Object in)
     {
-        return service.convert(desiredType, in);
+        return processor.convert(desiredType, in);
     }
 
     public CharSequence format(Object result, int inspect)
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Pipe.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Pipe.java
index 237b044..71a612f 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Pipe.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Pipe.java
@@ -100,7 +100,7 @@
         tIn.set(in);
         tOut.set(out);
         tErr.set(err);
-        closure.session().service.threadIO.setStreams(in, out, err);
+        closure.session().threadIO().setStreams(in, out, err);
         
         try
         {
@@ -120,7 +120,7 @@
         finally
         {
             out.flush();
-            closure.session().service.threadIO.close();
+            closure.session().threadIO().close();
 
             try
             {
diff --git a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/shell/Context.java b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/shell/Context.java
index 505713b..260d1f5 100644
--- a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/shell/Context.java
+++ b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/shell/Context.java
@@ -19,24 +19,25 @@
 package org.apache.felix.gogo.runtime.shell;
 
 import org.apache.felix.gogo.runtime.threadio.ThreadIOImpl;
+import org.osgi.service.command.CommandSession;
 
-public class Context extends CommandShellImpl
+public class Context extends CommandProcessorImpl
 {
     public static final String EMPTY = "";
-    CommandSessionImpl session = (CommandSessionImpl) createSession(System.in,
-        System.out, System.err);
-    static ThreadIOImpl threadio;
+    
+    private static final ThreadIOImpl threadio;
+    private final CommandSession session;
 
     static
     {
         threadio = new ThreadIOImpl();
         threadio.start();
-
     }
 
     public Context()
     {
-        setThreadio(threadio);
+        super(threadio);
+        session = (CommandSessionImpl) createSession(System.in, System.out, System.err);
     }
 
     public Object execute(CharSequence source) throws Exception
@@ -49,13 +50,14 @@
         finally
         {
             System.err.println("execute<" + source + "> = ("
-                + (null == result ? "Null" : result.getClass().getSimpleName()) + ")(" + result + ")\n");
+                + (null == result ? "Null" : result.getClass().getSimpleName()) + ")("
+                + result + ")\n");
         }
     }
 
-    public void addCommand(String name, Object target)
+    public void addCommand(String function, Object target)
     {
-        put("test:" + name, target);
+        addCommand("test", target, function);
     }
 
     public void set(String name, Object value)