FELIX-5077 / FELIX-3331 - more reliable shutdown

- fix some infrastructure/Maven related issues causing the build to break at
  unexpected moments;
- introduced a base test case that properly created and destroys the parsing
  context (prevents unexpected test failures);
- shut down the shell thread correctly;
- some other minor issues fixed.



git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1725510 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/gogo/gogo-parent/pom.xml b/gogo/gogo-parent/pom.xml
index 0fdf21a..c93ba1a 100644
--- a/gogo/gogo-parent/pom.xml
+++ b/gogo/gogo-parent/pom.xml
@@ -39,6 +39,12 @@
                 <version>4.5</version>
                 <scope>test</scope>
             </dependency>
+            <dependency>
+                <groupId>org.mockito</groupId>
+                <artifactId>mockito-core</artifactId>
+                <version>1.10.19</version>
+                <scope>test</scope>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 
diff --git a/gogo/runtime/pom.xml b/gogo/runtime/pom.xml
index 515095a..23a686c 100644
--- a/gogo/runtime/pom.xml
+++ b/gogo/runtime/pom.xml
@@ -1,86 +1,86 @@
-<!--
- 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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-    <parent>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>gogo-parent</artifactId>
-        <version>0.6.0</version>
-        <relativePath>../gogo-parent/pom.xml</relativePath>
-    </parent>
-    <modelVersion>4.0.0</modelVersion>
-    <packaging>bundle</packaging>
-    <name>Apache Felix Gogo Runtime</name>
-    <artifactId>org.apache.felix.gogo.runtime</artifactId>
-    <version>0.16.3-SNAPSHOT</version>
-    <dependencies>
-        <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.core</artifactId>
-            <version>4.0.0</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.compendium</artifactId>
-            <version>4.0.0</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-    </dependencies>
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.felix</groupId>
-                <artifactId>maven-bundle-plugin</artifactId>
-                <extensions>true</extensions>
-                <configuration>
-                    <instructions>
-                        <Export-Service>
-                            org.apache.felix.service.threadio.ThreadIO,
-                            org.apache.felix.service.command.CommandProcessor
-                        </Export-Service>
-                        <Export-Package>
-                            org.apache.felix.service.command;
-                            org.apache.felix.service.threadio; version=${project.version}; status="provisional"; mandatory:="status",
-                            org.apache.felix.gogo.api; version=${project.version}
-                        </Export-Package>
-                        <Import-Package>
-                            org.osgi.service.event*; resolution:=optional,
-                            org.osgi.service.log*; resolution:=optional,
-                            org.osgi.service.packageadmin*; resolution:=optional,
-                            org.osgi.service.startlevel*; resolution:=optional,
-                            *
-                        </Import-Package>
-                        <Private-Package>org.apache.felix.gogo.runtime*</Private-Package>
-                        <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
-                        <Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
-                        <Bundle-Activator>org.apache.felix.gogo.runtime.activator.Activator</Bundle-Activator>
-                        <Include-Resource>{maven-resources},META-INF/LICENSE=LICENSE,META-INF/NOTICE=NOTICE,META-INF/DEPENDENCIES=DEPENDENCIES</Include-Resource>
-                        <_versionpolicy>[$(version;==;$(@)),$(version;+;$(@)))</_versionpolicy>
-                        <_removeheaders>Private-Package,Ignore-Package,Include-Resource</_removeheaders>
-                    </instructions>
-                </configuration>
-            </plugin>
-        </plugins>
-    </build>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<parent>
+		<groupId>org.apache.felix</groupId>
+		<artifactId>gogo-parent</artifactId>
+		<version>0.6.0</version>
+		<relativePath>../gogo-parent/pom.xml</relativePath>
+	</parent>
+	<modelVersion>4.0.0</modelVersion>
+	<packaging>bundle</packaging>
+	<name>Apache Felix Gogo Runtime</name>
+	<artifactId>org.apache.felix.gogo.runtime</artifactId>
+	<version>0.16.3-SNAPSHOT</version>
+	<dependencies>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.core</artifactId>
+			<version>4.0.0</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.compendium</artifactId>
+			<version>4.0.0</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.mockito</groupId>
+			<artifactId>mockito-core</artifactId>
+			<version>1.10.19</version>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.felix</groupId>
+				<artifactId>maven-bundle-plugin</artifactId>
+				<extensions>true</extensions>
+				<configuration>
+					<instructions>
+						<Export-Service>
+							org.apache.felix.service.threadio.ThreadIO,
+							org.apache.felix.service.command.CommandProcessor
+						</Export-Service>
+						<Export-Package>
+							org.apache.felix.service.command;
+							org.apache.felix.service.threadio; version=${project.version}; status="provisional";
+							mandatory:="status",
+							org.apache.felix.gogo.api; version=${project.version}
+						</Export-Package>
+						<Import-Package>
+							org.osgi.service.event*; resolution:=optional,
+							org.osgi.service.log*; resolution:=optional,
+							org.osgi.service.packageadmin*; resolution:=optional,
+							org.osgi.service.startlevel*; resolution:=optional,
+							*
+						</Import-Package>
+						<Private-Package>org.apache.felix.gogo.runtime*</Private-Package>
+						<Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
+						<Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
+						<Bundle-Activator>org.apache.felix.gogo.runtime.activator.Activator</Bundle-Activator>
+						<Include-Resource>{maven-resources},META-INF/LICENSE=LICENSE,META-INF/NOTICE=NOTICE,META-INF/DEPENDENCIES=DEPENDENCIES</Include-Resource>
+						<_versionpolicy>[$(version;==;$(@)),$(version;+;$(@)))</_versionpolicy>
+						<_removeheaders>Private-Package,Ignore-Package,Include-Resource</_removeheaders>
+					</instructions>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
 </project>
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/CommandProcessorImpl.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/CommandProcessorImpl.java
index 9edc62c..7f987dc 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/CommandProcessorImpl.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/CommandProcessorImpl.java
@@ -68,7 +68,7 @@
         }
     }
 
-    void closeSession(CommandSessionImpl session)
+    void removeSession(CommandSessionImpl session)
     {
         synchronized (sessions)
         {
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/CommandSessionImpl.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/CommandSessionImpl.java
index 30f521a..9935981 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/CommandSessionImpl.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/CommandSessionImpl.java
@@ -21,10 +21,11 @@
 // DWB10: add SCOPE support: https://www.osgi.org/bugzilla/show_bug.cgi?id=51
 package org.apache.felix.gogo.runtime;
 
+import java.io.Closeable;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintStream;
 import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -48,7 +49,7 @@
 
     protected InputStream in;
     protected PrintStream out;
-    PrintStream err;
+    protected PrintStream err;
 
     private final CommandProcessorImpl processor;
     protected final Map<String, Object> variables = new HashMap<String, Object>();
@@ -71,8 +72,10 @@
     {
         if (!this.closed)
         {
-            this.processor.closeSession(this);
+            this.processor.removeSession(this);
             this.closed = true;
+
+            this.in = closeSilently(in);
         }
     }
 
@@ -344,18 +347,21 @@
     {
         boolean found = false;
         Formatter f = new Formatter();
+        f.close();
+
         Method methods[] = b.getClass().getMethods();
         for (Method m : methods)
         {
             try
             {
                 String name = m.getName();
-                if (m.getName().startsWith("get") && !m.getName().equals("getClass") && m.getParameterTypes().length == 0 && Modifier.isPublic(m.getModifiers()))
+                if (!name.equals("getClass") && name.startsWith("get") && m.getParameterTypes().length == 0)
                 {
+                    m.setAccessible(true);
+                    Object value = m.invoke(b);
+
                     found = true;
                     name = name.substring(3);
-                    m.setAccessible(true);
-                    Object value = m.invoke(b, (Object[]) null);
                     f.format(COLUMN, name, format(value, Converter.LINE, this));
                 }
             }
@@ -400,4 +406,19 @@
         return processor.expr(this, expr);
     }
 
+    private static <T extends Closeable> T closeSilently(T resource)
+    {
+        if (resource != null)
+        {
+            try
+            {
+                resource.close();
+            }
+            catch (IOException e)
+            {
+                // Ignore, nothing we can do here...
+            }
+        }
+        return resource;
+    }
 }
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Parser.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Parser.java
index 23c2f65..fce18d6 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Parser.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Parser.java
@@ -38,31 +38,33 @@
     public List<List<List<Token>>> program()
     {
         List<List<List<Token>>> program = new ArrayList<List<List<Token>>>();
-        
-        while (tz.next() != Type.EOT)
+
+        outer: while (tz.next() != Type.EOT)
         {
             program.add(pipeline());
-            
+
             switch (tz.type())
             {
                 case SEMICOLON:
                 case NEWLINE:
                     continue;
+
+                default:
+                    break outer;
             }
-            
-            break;
         }
 
         if (tz.next() != Type.EOT)
+        {
             throw new RuntimeException("Program has trailing text: " + tz.value());
-
+        }
         return program;
     }
 
     private List<List<Token>> pipeline()
     {
         List<List<Token>> pipeline = new ArrayList<List<Token>>();
-        
+
         while (true)
         {
             pipeline.add(command());
@@ -89,7 +91,7 @@
         while (true)
         {
             Token t = tz.token();
-            
+
             switch (t.type)
             {
                 case WORD:
@@ -99,13 +101,13 @@
                 case ASSIGN:
                 case EXPR:
                     break;
-                    
+
                 default:
                     throw new SyntaxError(t.line, t.column, "unexpected token: " + t.type);
             }
-            
+
             command.add(t);
-            
+
             switch (tz.next())
             {
                 case PIPE:
@@ -113,10 +115,13 @@
                 case NEWLINE:
                 case EOT:
                     return command;
+
+                default:
+                    break;
             }
         }
     }
-    
+
     public void array(List<Token> list, Map<Token, Token> map) throws Exception
     {
         Token lt = null;
diff --git a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/BaseTestCase.java b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/BaseTestCase.java
new file mode 100644
index 0000000..488e49b
--- /dev/null
+++ b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/BaseTestCase.java
@@ -0,0 +1,42 @@
+/*
+ * 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;
+
+import junit.framework.TestCase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+abstract class BaseTestCase extends TestCase
+{
+    protected Context m_ctx;
+
+    @Override
+    protected final void setUp() throws Exception
+    {
+        m_ctx = new Context(true);
+    }
+
+    @Override
+    protected final void tearDown() throws Exception
+    {
+        m_ctx.stop();
+    }
+}
diff --git a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/Context.java b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/Context.java
index b96a313..bd40bf2 100644
--- a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/Context.java
+++ b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/Context.java
@@ -24,7 +24,7 @@
 public class Context extends CommandProcessorImpl
 {
     public static final String EMPTY = "";
-    
+
     private static final ThreadIOImpl threadio;
     private final CommandSession session;
 
@@ -34,7 +34,7 @@
         threadio.start();
     }
 
-    public Context()
+    public Context(boolean foo)
     {
         super(threadio);
         addCommand("osgi", this, "addCommand");
@@ -43,6 +43,19 @@
         session = (CommandSessionImpl) createSession(System.in, System.out, System.err);
     }
 
+    @Override
+    public void stop()
+    {
+        try
+        {
+            super.stop();
+        }
+        finally
+        {
+            threadio.stop();
+        }
+    }
+
     public Object execute(CharSequence source) throws Exception
     {
         Object result = new Exception();
diff --git a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestCoercion.java b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestCoercion.java
index ccd7d44..7ab32a1 100644
--- a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestCoercion.java
+++ b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestCoercion.java
@@ -18,13 +18,11 @@
  */
 package org.apache.felix.gogo.runtime;
 
-import junit.framework.TestCase;
-
 import org.apache.felix.service.command.CommandSession;
 import org.apache.felix.service.command.Descriptor;
 import org.apache.felix.service.command.Parameter;
 
-public class TestCoercion extends TestCase
+public class TestCoercion extends BaseTestCase
 {
     public boolean fBool(boolean t)
     {
@@ -59,32 +57,31 @@
 
     public void testSimpleTypes() throws Exception
     {
-        Context c = new Context();
-        c.addCommand("fBool", this);
-        c.addCommand("fDouble", this);
-        c.addCommand("fInt", this);
-        c.addCommand("fLong", this);
-        c.addCommand("fString", this);
+        m_ctx.addCommand("fBool", this);
+        m_ctx.addCommand("fDouble", this);
+        m_ctx.addCommand("fInt", this);
+        m_ctx.addCommand("fLong", this);
+        m_ctx.addCommand("fString", this);
 
-        assertEquals("fBool true", true, c.execute("fBool true"));
-        assertEquals("fBool 'false'", false, c.execute("fBool 'false'"));
-        
-        assertEquals("fDouble 11", 11.0, c.execute("fDouble 11"));
-        assertEquals("fDouble '11'", 11.0, c.execute("fDouble '11'"));
-        
-        assertEquals("fInt 22", 22, c.execute("fInt 22"));
-        assertEquals("fInt '23'", 23, c.execute("fInt '23'"));
-        assertEquals("fInt 1 2", "array", c.execute("fInt 1 2"));
-        
-        assertEquals("fLong 33", 33L, c.execute("fLong 33"));
-        assertEquals("fLong '34'", 34L, c.execute("fLong '34'"));
-        
-        assertEquals("fString wibble", "wibble", c.execute("fString wibble"));
-        assertEquals("fString 'wibble'", "wibble", c.execute("fString 'wibble'"));
+        assertEquals("fBool true", true, m_ctx.execute("fBool true"));
+        assertEquals("fBool 'false'", false, m_ctx.execute("fBool 'false'"));
+
+        assertEquals("fDouble 11", 11.0, m_ctx.execute("fDouble 11"));
+        assertEquals("fDouble '11'", 11.0, m_ctx.execute("fDouble '11'"));
+
+        assertEquals("fInt 22", 22, m_ctx.execute("fInt 22"));
+        assertEquals("fInt '23'", 23, m_ctx.execute("fInt '23'"));
+        assertEquals("fInt 1 2", "array", m_ctx.execute("fInt 1 2"));
+
+        assertEquals("fLong 33", 33L, m_ctx.execute("fLong 33"));
+        assertEquals("fLong '34'", 34L, m_ctx.execute("fLong '34'"));
+
+        assertEquals("fString wibble", "wibble", m_ctx.execute("fString wibble"));
+        assertEquals("fString 'wibble'", "wibble", m_ctx.execute("fString 'wibble'"));
 
         try
         {
-            Object r = c.execute("fString ");
+            Object r = m_ctx.execute("fString ");
             fail("too few args: expected IllegalArgumentException, got: " + r);
         }
         catch (IllegalArgumentException e)
@@ -93,7 +90,7 @@
 
         try
         {
-            Object r = c.execute("fString a b");
+            Object r = m_ctx.execute("fString a b");
             fail("too many args: expected IllegalArgumentException, got: " + r);
         }
         catch (IllegalArgumentException e)
@@ -102,7 +99,7 @@
 
         try
         {
-            Object r = c.execute("fLong string");
+            Object r = m_ctx.execute("fLong string");
             fail("wrong arg type: expected IllegalArgumentException, got: " + r);
         }
         catch (IllegalArgumentException e)
@@ -122,12 +119,11 @@
 
     public void testBestCoercion() throws Exception
     {
-        Context c = new Context();
-        c.addCommand("bundles", this);
+        m_ctx.addCommand("bundles", this);
 
-        assertEquals("bundles myloc", "string", c.execute("bundles myloc"));
-        assertEquals("bundles 1", "long", c.execute("bundles 1"));
-        assertEquals("bundles '1'", "string", c.execute("bundles '1'"));
+        assertEquals("bundles myloc", "string", m_ctx.execute("bundles myloc"));
+        assertEquals("bundles 1", "long", m_ctx.execute("bundles 1"));
+        assertEquals("bundles '1'", "string", m_ctx.execute("bundles '1'"));
     }
 
     @Descriptor("list all installed bundles")
@@ -149,24 +145,23 @@
 
     public void testParameter0() throws Exception
     {
-        Context c = new Context();
-        c.addCommand("p0", this);
-        c.addCommand("p01", this);
+        m_ctx.addCommand("p0", this);
+        m_ctx.addCommand("p01", this);
 
-        assertEquals("p0", "false:false", c.execute("p0"));
-        assertEquals("p0 -l", "true:false", c.execute("p0 -l"));
-        assertEquals("p0 --location", "true:false", c.execute("p0 --location"));
-        assertEquals("p0 -l -s", "true:true", c.execute("p0 -l -s"));
-        assertEquals("p0 -s -l", "true:true", c.execute("p0 -s -l"));
+        assertEquals("p0", "false:false", m_ctx.execute("p0"));
+        assertEquals("p0 -l", "true:false", m_ctx.execute("p0 -l"));
+        assertEquals("p0 --location", "true:false", m_ctx.execute("p0 --location"));
+        assertEquals("p0 -l -s", "true:true", m_ctx.execute("p0 -l -s"));
+        assertEquals("p0 -s -l", "true:true", m_ctx.execute("p0 -s -l"));
         try
         {
-            Object r = c.execute("p0 wibble");
+            Object r = m_ctx.execute("p0 wibble");
             fail("too many args: expected IllegalArgumentException, got: " + r);
         }
         catch (IllegalArgumentException e)
         {
         }
-        assertEquals("p01 -f", true, c.execute("p01 -f"));
+        assertEquals("p01 -f", true, m_ctx.execute("p01 -f"));
     }
 
     public String p1(
@@ -177,17 +172,16 @@
 
     public void testParameter1() throws Exception
     {
-        Context c = new Context();
-        c.addCommand("p1", this);
+        m_ctx.addCommand("p1", this);
 
-        assertEquals("no parameter", "absent", c.execute("p1"));
+        assertEquals("no parameter", "absent", m_ctx.execute("p1"));
 
         // FELIX-2894
-        assertEquals("correct parameter", "wibble", c.execute("p1 -p wibble"));
+        assertEquals("correct parameter", "wibble", m_ctx.execute("p1 -p wibble"));
 
         try
         {
-            Object r = c.execute("p1 -p");
+            Object r = m_ctx.execute("p1 -p");
             fail("missing parameter: expected IllegalArgumentException, got: " + r);
         }
         catch (IllegalArgumentException e)
@@ -196,12 +190,11 @@
 
         try
         {
-            Object r = c.execute("p1 -X");
+            Object r = m_ctx.execute("p1 -X");
             fail("wrong parameter: expected IllegalArgumentException, got: " + r);
         }
         catch (IllegalArgumentException e)
         {
         }
     }
-
 }
diff --git a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser.java b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser.java
index 13c52f1..7250b0a 100644
--- a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser.java
+++ b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser.java
@@ -18,13 +18,6 @@
  */
 package org.apache.felix.gogo.runtime;
 
-import junit.framework.TestCase;
-
-import org.apache.felix.gogo.runtime.Parser;
-import org.apache.felix.gogo.runtime.Token;
-import org.apache.felix.service.command.CommandSession;
-import org.apache.felix.service.command.Function;
-
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
@@ -34,27 +27,28 @@
 import java.util.List;
 import java.util.regex.Pattern;
 
-public class TestParser extends TestCase
+import org.apache.felix.service.command.CommandSession;
+import org.apache.felix.service.command.Function;
+
+public class TestParser extends BaseTestCase
 {
     int beentheredonethat = 0;
 
     public void testEvaluatation() throws Exception
     {
-        Context c = new Context();
-        c.addCommand("echo", this);
-        c.addCommand("capture", this);
+        m_ctx.addCommand("echo", this);
+        m_ctx.addCommand("capture", this);
 
-        assertEquals("a", c.execute("echo a | capture"));
-        assertEquals("a", c.execute("(echo a) | capture"));
-        assertEquals("a", c.execute("((echo a)) | capture"));
+        assertEquals("a", m_ctx.execute("echo a | capture"));
+        assertEquals("a", m_ctx.execute("(echo a) | capture"));
+        assertEquals("a", m_ctx.execute("((echo a)) | capture"));
     }
 
     public void testUnknownCommand() throws Exception
     {
-        Context c = new Context();
         try
         {
-            c.execute("echo");
+            m_ctx.execute("echo");
             fail("Execution should have failed due to missing command");
         }
         catch (IllegalArgumentException e)
@@ -65,111 +59,103 @@
 
     public void testSpecialValues() throws Exception
     {
-        Context c = new Context();
-        assertEquals(false, c.execute("false"));
-        assertEquals(true, c.execute("true"));
-        assertEquals(null, c.execute("null"));
+        assertEquals(false, m_ctx.execute("false"));
+        assertEquals(true, m_ctx.execute("true"));
+        assertEquals(null, m_ctx.execute("null"));
     }
 
     public void testQuotes() throws Exception
     {
-        Context c = new Context();
-        c.addCommand("echo", this);
-        c.set("c", "a");
+        m_ctx.addCommand("echo", this);
+        m_ctx.set("c", "a");
 
-        assertEquals("a b", c.execute("echo a b"));
-        assertEquals("a b", c.execute("echo 'a b'"));
-        assertEquals("a b", c.execute("echo \"a b\""));
-        assertEquals("a b", c.execute("echo a  b"));
-        assertEquals("a  b", c.execute("echo 'a  b'"));
-        assertEquals("a  b", c.execute("echo \"a  b\""));
-        assertEquals("a b", c.execute("echo $c  b"));
-        assertEquals("$c  b", c.execute("echo '$c  b'"));
-        assertEquals("a  b", c.execute("echo \"$c  b\""));
-        assertEquals("a b", c.execute("echo ${c}  b"));
-        assertEquals("${c}  b", c.execute("echo '${c}  b'"));
-        assertEquals("a  b", c.execute("echo \"${c}  b\""));
-        assertEquals("aa", c.execute("echo $c$c"));
-        assertEquals("a ;a", c.execute("echo a\\ \\;a"));
-        assertEquals("baabab", c.execute("echo b${c}${c}b${c}b"));
+        assertEquals("a b", m_ctx.execute("echo a b"));
+        assertEquals("a b", m_ctx.execute("echo 'a b'"));
+        assertEquals("a b", m_ctx.execute("echo \"a b\""));
+        assertEquals("a b", m_ctx.execute("echo a  b"));
+        assertEquals("a  b", m_ctx.execute("echo 'a  b'"));
+        assertEquals("a  b", m_ctx.execute("echo \"a  b\""));
+        assertEquals("a b", m_ctx.execute("echo $c  b"));
+        assertEquals("$c  b", m_ctx.execute("echo '$c  b'"));
+        assertEquals("a  b", m_ctx.execute("echo \"$c  b\""));
+        assertEquals("a b", m_ctx.execute("echo ${c}  b"));
+        assertEquals("${c}  b", m_ctx.execute("echo '${c}  b'"));
+        assertEquals("a  b", m_ctx.execute("echo \"${c}  b\""));
+        assertEquals("aa", m_ctx.execute("echo $c$c"));
+        assertEquals("a ;a", m_ctx.execute("echo a\\ \\;a"));
+        assertEquals("baabab", m_ctx.execute("echo b${c}${c}b${c}b"));
 
-        c.set("d", "a  b ");
-        assertEquals("a  b ", c.execute("echo \"$d\""));
+        m_ctx.set("d", "a  b ");
+        assertEquals("a  b ", m_ctx.execute("echo \"$d\""));
     }
 
     public void testScope() throws Exception
     {
-        Context c = new Context();
-        c.addCommand("echo", this);
-        assertEquals("$a", c.execute("test:echo \\$a"));
-        assertEquals("file://poo", c.execute("test:echo file://poo"));
+        m_ctx.addCommand("echo", this);
+        assertEquals("$a", m_ctx.execute("test:echo \\$a"));
+        assertEquals("file://poo", m_ctx.execute("test:echo file://poo"));
     }
 
     public void testPipe() throws Exception
     {
-        Context c = new Context();
-        c.addCommand("echo", this);
-        c.addCommand("capture", this);
-        c.addCommand("grep", this);
-        c.addCommand("echoout", this);
-        c.execute("myecho = { echoout $args }");
-        assertEquals("def", c.execute("echo def|grep d.*|capture"));
-        assertEquals("def", c.execute("echoout def|grep d.*|capture"));
-        assertEquals("def", c.execute("myecho def|grep d.*|capture"));
+        m_ctx.addCommand("echo", this);
+        m_ctx.addCommand("capture", this);
+        m_ctx.addCommand("grep", this);
+        m_ctx.addCommand("echoout", this);
+        m_ctx.execute("myecho = { echoout $args }");
+        assertEquals("def", m_ctx.execute("echo def|grep d.*|capture"));
+        assertEquals("def", m_ctx.execute("echoout def|grep d.*|capture"));
+        assertEquals("def", m_ctx.execute("myecho def|grep d.*|capture"));
         assertEquals("def",
-            c.execute("(echoout abc; echoout def; echoout ghi)|grep d.*|capture"));
-        assertEquals("", c.execute("echoout def; echoout ghi | grep d.* | capture"));
-        assertEquals("hello world", c.execute("echo hello world|capture"));
+            m_ctx.execute("(echoout abc; echoout def; echoout ghi)|grep d.*|capture"));
+        assertEquals("", m_ctx.execute("echoout def; echoout ghi | grep d.* | capture"));
+        assertEquals("hello world", m_ctx.execute("echo hello world|capture"));
         assertEquals("defghi",
-            c.execute("(echoout abc; echoout def; echoout ghi)|grep 'def|ghi'|capture"));
+            m_ctx.execute("(echoout abc; echoout def; echoout ghi)|grep 'def|ghi'|capture"));
     }
 
     public void testAssignment() throws Exception
     {
-        Context c = new Context();
-        c.addCommand("echo", this);
-        c.addCommand("grep", this);
-        assertEquals("a", c.execute("a = a; echo ${$a}"));
+        m_ctx.addCommand("echo", this);
+        m_ctx.addCommand("grep", this);
+        assertEquals("a", m_ctx.execute("a = a; echo ${$a}"));
 
-        assertEquals("hello", c.execute("echo hello"));
-        assertEquals("hello", c.execute("a = (echo hello)"));
-      //assertEquals("a", c.execute("a = a; echo $(echo a)")); // #p2 - no eval in var expansion
-        assertEquals("3", c.execute("a=3; echo $a"));
-        assertEquals("3", c.execute("a = 3; echo $a"));
-        assertEquals("a", c.execute("a = a; echo ${$a}"));
+        assertEquals("hello", m_ctx.execute("echo hello"));
+        assertEquals("hello", m_ctx.execute("a = (echo hello)"));
+        //assertEquals("a", m_ctx.execute("a = a; echo $(echo a)")); // #p2 - no eval in var expansion
+        assertEquals("3", m_ctx.execute("a=3; echo $a"));
+        assertEquals("3", m_ctx.execute("a = 3; echo $a"));
+        assertEquals("a", m_ctx.execute("a = a; echo ${$a}"));
     }
 
     public void testComment() throws Exception
     {
-        Context c = new Context();
-        c.addCommand("echo", this);
-        assertEquals("1", c.execute("echo 1 // hello"));
+        m_ctx.addCommand("echo", this);
+        assertEquals("1", m_ctx.execute("echo 1 // hello"));
     }
 
     public void testClosure() throws Exception
     {
-        Context c = new Context();
-        c.addCommand("echo", this);
-        c.addCommand("capture", this);
+        m_ctx.addCommand("echo", this);
+        m_ctx.addCommand("capture", this);
 
-        assertEquals("a", c.execute("e = { echo $1 } ; e a   b"));
-        assertEquals("b", c.execute("e = { echo $2 } ; e a   b"));
-        assertEquals("b", c.execute("e = { eval $args } ; e echo  b"));
-        assertEquals("ca b", c.execute("e = { echo c$args } ; e a  b"));
-        assertEquals("c a b", c.execute("e = { echo c $args } ; e a  b"));
-        assertEquals("ca  b", c.execute("e = { echo c$args } ; e 'a  b'"));
+        assertEquals("a", m_ctx.execute("e = { echo $1 } ; e a   b"));
+        assertEquals("b", m_ctx.execute("e = { echo $2 } ; e a   b"));
+        assertEquals("b", m_ctx.execute("e = { eval $args } ; e echo  b"));
+        assertEquals("ca b", m_ctx.execute("e = { echo c$args } ; e a  b"));
+        assertEquals("c a b", m_ctx.execute("e = { echo c $args } ; e a  b"));
+        assertEquals("ca  b", m_ctx.execute("e = { echo c$args } ; e 'a  b'"));
     }
 
     public void testArray() throws Exception
     {
-        Context c = new Context();
-        c.set("echo", true);
+        m_ctx.set("echo", this);
         assertEquals("http://www.aqute.biz?com=2&biz=1",
-            c.execute("['http://www.aqute.biz?com=2&biz=1'] get 0"));
-        assertEquals("{a=2, b=3}", c.execute("[a=2 b=3]").toString());
-        assertEquals(3L, c.execute("[a=2 b=3] get b"));
-        assertEquals("[3, 4]", c.execute("[1 2 [3 4] 5 6] get 2").toString());
-        assertEquals(5, c.execute("[1 2 [3 4] 5 6] size"));
+            m_ctx.execute("['http://www.aqute.biz?com=2&biz=1'] get 0"));
+        assertEquals("{a=2, b=3}", m_ctx.execute("[a=2 b=3]").toString());
+        assertEquals(3L, m_ctx.execute("[a=2 b=3] get b"));
+        assertEquals("[3, 4]", m_ctx.execute("[1 2 [3 4] 5 6] get 2").toString());
+        assertEquals(5, m_ctx.execute("[1 2 [3 4] 5 6] size"));
     }
 
     public void testParentheses()
@@ -185,9 +171,8 @@
 
     public void testEcho() throws Exception
     {
-        Context c = new Context();
-        c.addCommand("echo", this);
-        c.execute("echo peter");
+        m_ctx.addCommand("echo", this);
+        m_ctx.execute("echo peter");
     }
 
     public void grep(String match) throws IOException
@@ -220,22 +205,20 @@
 
     public void testVars() throws Exception
     {
-        Context c = new Context();
-        c.addCommand("echo", this);
+        m_ctx.addCommand("echo", this);
 
-        assertEquals("", c.execute("echo ${very.likely.that.this.does.not.exist}"));
-        assertNotNull(c.execute("echo ${java.shell.name}"));
-        assertEquals("a", c.execute("a = a; echo ${a}"));
+        assertEquals("", m_ctx.execute("echo ${very.likely.that.this.does.not.exist}"));
+        assertNotNull(m_ctx.execute("echo ${java.shell.name}"));
+        assertEquals("a", m_ctx.execute("a = a; echo ${a}"));
     }
 
     public void testFunny() throws Exception
     {
-        Context c = new Context();
-        c.addCommand("echo", this);
-        assertEquals("a", c.execute("echo a") + "");
-        assertEquals("a", c.execute("eval (echo echo) a") + "");
-        //assertEquals("a", c.execute("((echo echo) echo) (echo a)") + "");
-        assertEquals("3", c.execute("[a=2 (echo b)=(echo 3)] get b").toString());
+        m_ctx.addCommand("echo", this);
+        assertEquals("a", m_ctx.execute("echo a") + "");
+        assertEquals("a", m_ctx.execute("eval (echo echo) a") + "");
+        //assertEquals("a", m_ctx.execute("((echo echo) echo) (echo a)") + "");
+        assertEquals("3", m_ctx.execute("[a=2 (echo b)=(echo 3)] get b").toString());
     }
 
     public CharSequence echo(Object args[])
@@ -265,22 +248,21 @@
 
     public void testContext() throws Exception
     {
-        Context c = new Context();
-        c.addCommand("ls", this);
+        m_ctx.addCommand("ls", this);
         beentheredonethat = 0;
-        c.execute("ls");
+        m_ctx.execute("ls");
         assertEquals(1, beentheredonethat);
 
         beentheredonethat = 0;
-        c.execute("ls 10");
+        m_ctx.execute("ls 10");
         assertEquals(10, beentheredonethat);
 
         beentheredonethat = 0;
-        c.execute("ls a b c d e f g h i j");
+        m_ctx.execute("ls a b c d e f g h i j");
         assertEquals(10, beentheredonethat);
 
         beentheredonethat = 0;
-        Integer result = (Integer) c.execute("ls (ls 5)");
+        Integer result = (Integer) m_ctx.execute("ls (ls 5)");
         assertEquals(10, beentheredonethat);
         assertEquals((Integer) 5, result);
     }
@@ -336,8 +318,7 @@
     public void testSimpleValue()
     {
         List<Token> x = new Parser(
-            "abc def.ghi http://www.osgi.org?abc=&x=1 [1,2,3] {{{{{{{xyz}}}}}}} (immediate) {'{{{{{'} {\\{} 'abc{}'")
-            .program().get(0).get(0);
+            "abc def.ghi http://www.osgi.org?abc=&x=1 [1,2,3] {{{{{{{xyz}}}}}}} (immediate) {'{{{{{'} {\\{} 'abc{}'").program().get(0).get(0);
         assertEquals("abc", x.get(0).toString());
         assertEquals("def.ghi", x.get(1).toString());
         assertEquals("http://www.osgi.org?abc=&x=1", x.get(2).toString());
diff --git a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser2.java b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser2.java
index 350f9f6..4df5dab 100644
--- a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser2.java
+++ b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser2.java
@@ -20,26 +20,23 @@
 
 import java.io.EOFException;
 
-import junit.framework.TestCase;
-
 /*
  * Test features of the new parser/tokenizer, many of which are not supported
  * by the original parser.
  */
-public class TestParser2 extends TestCase
+public class TestParser2 extends BaseTestCase
 {
     public void testComment() throws Exception
     {
-        Context c = new Context();
-        c.addCommand("echo", this);
+        m_ctx.addCommand("echo", this);
 
-        assertEquals("file://wibble#tag", c.execute("echo file://wibble#tag"));
-        assertEquals("file:", c.execute("echo file: //wibble#tag"));
+        assertEquals("file://wibble#tag", m_ctx.execute("echo file://wibble#tag"));
+        assertEquals("file:", m_ctx.execute("echo file: //wibble#tag"));
 
-        assertEquals("PWD/*.java", c.execute("echo PWD/*.java"));
+        assertEquals("PWD/*.java", m_ctx.execute("echo PWD/*.java"));
         try
         {
-            c.execute("echo PWD /*.java");
+            m_ctx.execute("echo PWD /*.java");
             fail("expected EOFException");
         }
         catch (EOFException e)
@@ -47,44 +44,42 @@
             // expected
         }
 
-        assertEquals("ok", c.execute("// can't quote\necho ok\n"));
+        assertEquals("ok", m_ctx.execute("// can't quote\necho ok\n"));
 
         // quote in comment in closure
-        assertEquals("ok", c.execute("x = { // can't quote\necho ok\n}; x"));
-        assertEquals("ok", c.execute("x = {\n// can't quote\necho ok\n}; x"));
-        assertEquals("ok", c.execute("x = {// can't quote\necho ok\n}; x"));
+        assertEquals("ok", m_ctx.execute("x = { // can't quote\necho ok\n}; x"));
+        assertEquals("ok", m_ctx.execute("x = {\n// can't quote\necho ok\n}; x"));
+        assertEquals("ok", m_ctx.execute("x = {// can't quote\necho ok\n}; x"));
     }
 
     public void testCoercion() throws Exception
     {
-        Context c = new Context();
-        c.addCommand("echo", this);
+        m_ctx.addCommand("echo", this);
         // FELIX-2432
-        assertEquals("null x", c.execute("echo $expandsToNull x"));
+        assertEquals("null x", m_ctx.execute("echo $expandsToNull x"));
     }
 
     public void testStringExecution() throws Exception
     {
-        Context c = new Context();
-        c.addCommand("echo", this);
-        c.addCommand("new", this);
+        m_ctx.addCommand("echo", this);
+        m_ctx.addCommand("new", this);
         
         // FELIX-2433
-        assertEquals("helloworld", c.execute("echo \"$(echo hello)world\""));
+        assertEquals("helloworld", m_ctx.execute("echo \"$(echo hello)world\""));
         
          // FELIX-1473 - allow method calls on String objects
-        assertEquals("hello", c.execute("cmd = echo; eval $cmd hello"));
-        assertEquals(4, c.execute("'four' length"));
+        assertEquals("hello", m_ctx.execute("cmd = echo; eval $cmd hello"));
+        assertEquals(4, m_ctx.execute("'four' length"));
         try {
-            c.execute("four length");
+            m_ctx.execute("four length");
             fail("expected: command not found: four");
         } catch (IllegalArgumentException e) {
         }
         
         // check CharSequence types are preserved
-        Object b = c.execute("b = new java.lang.StringBuilder");
+        Object b = m_ctx.execute("b = new java.lang.StringBuilder");
         assertTrue(b instanceof StringBuilder);
-        assertEquals(b, c.execute("c = $b"));
+        assertEquals(b, m_ctx.execute("c = $b"));
     }
 
     public CharSequence echo(Object args[])
diff --git a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser3.java b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser3.java
index 3ffd2bc..2bfdbf8 100644
--- a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser3.java
+++ b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser3.java
@@ -18,27 +18,29 @@
  */
 package org.apache.felix.gogo.runtime;
 
-import junit.framework.TestCase;
-
-import java.io.EOFException;
-
 /*
  * Test features of the new parser/tokenizer, many of which are not supported
  * by the original parser.
  */
-public class TestParser3 extends TestCase
+public class TestParser3 extends BaseTestCase
 {
     public void testArithmetic() throws Exception
     {
-        Context c = new Context();
-        c.addCommand("echo", this);
+        m_ctx.addCommand("echo", this);
 
-        assertEquals("10d", c.execute("echo %(2*(3+2))d"));
-        assertEquals(3l, c.execute("%(1+2)"));
+        try
+        {
+            assertEquals("10d", m_ctx.execute("echo %(2*(3+2))d"));
+            assertEquals(3l, m_ctx.execute("%(1+2)"));
 
-        c.set("a", 2l);
-        assertEquals(3l, c.execute("%(a+=1)"));
-        assertEquals(3l, c.get("a"));
+            m_ctx.set("a", 2l);
+            assertEquals(3l, m_ctx.execute("%(a+=1)"));
+            assertEquals(3l, m_ctx.get("a"));
+        }
+        finally
+        {
+            m_ctx.stop();
+        }
     }
 
     public CharSequence echo(Object args[])
diff --git a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestTokenizer.java b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestTokenizer.java
index da1f959..434d280 100644
--- a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestTokenizer.java
+++ b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestTokenizer.java
@@ -28,15 +28,11 @@
 import java.util.HashMap;
 import java.util.Map;
 
-import junit.framework.TestCase;
-
 import org.apache.felix.gogo.runtime.Tokenizer.Type;
-import org.apache.felix.gogo.runtime.threadio.ThreadIOImpl;
-import org.junit.Test;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 
-public class TestTokenizer extends TestCase
+public class TestTokenizer extends BaseTestCase
 {
     private final Map<String, Object> vars = new HashMap<String, Object>();
     private final Evaluate evaluate;
@@ -62,7 +58,6 @@
         };
     }
 
-    @Test
     public void testHello() throws Exception
     {
         testHello("hello world\n");
@@ -103,8 +98,7 @@
         assertEquals(Type.NEWLINE, t.next());
         assertEquals(Type.EOT, t.next());
     }
-    
-    @Test
+
     public void testString() throws Exception
     {
         testString("'single $quote' \"double $quote\"\n");
@@ -122,7 +116,6 @@
         assertEquals(Type.EOT, t.next());
     }
 
-    @Test
     public void testClosure() throws Exception
     {
         testClosure2("x = { echo '}' $args //comment's\n}\n");
@@ -134,7 +127,6 @@
     /*
      * x = {echo $args};
      */
-    @Test
     private void testClosure2(CharSequence text) throws Exception
     {
         Tokenizer t = new Tokenizer(text);
@@ -155,7 +147,6 @@
         return type;
     }
 
-    @Test
     public void testExpand() throws Exception
     {
         final URI home = new URI("/home/derek");
@@ -278,7 +269,6 @@
         return Tokenizer.expand(word, evaluate);
     }
 
-    @Test
     public void testParser() throws Exception
     {
         new Parser("// comment\n" + "a=\"who's there?\"; ps -ef;\n" + "ls | \n grep y\n").program();
@@ -290,31 +280,19 @@
     /**
      * FELIX-4679 / FELIX-4671. 
      */
-    @Test
     public void testScriptFelix4679() throws Exception
     {
         String script = "addcommand system (((${.context} bundles) 0) loadclass java.lang.System)";
 
-        ThreadIOImpl tio = new ThreadIOImpl();
-        tio.start();
+        BundleContext bc = createMockContext();
 
-        try
-        {
-            BundleContext bc = createMockContext();
+        m_ctx.addCommand("gogo", m_ctx, "addcommand");
+        m_ctx.addConstant(".context", bc);
 
-            CommandProcessorImpl processor = new CommandProcessorImpl(tio);
-            processor.addCommand("gogo", processor, "addcommand");
-            processor.addConstant(".context", bc);
+        CommandSessionImpl session = new CommandSessionImpl(m_ctx, new ByteArrayInputStream(script.getBytes()), System.out, System.err);
 
-            CommandSessionImpl session = new CommandSessionImpl(processor, new ByteArrayInputStream(script.getBytes()), System.out, System.err);
-
-            Closure c = new Closure(session, null, script);
-            assertNull(c.execute(session, null));
-        }
-        finally
-        {
-            tio.stop();
-        }
+        Closure c = new Closure(session, null, script);
+        assertNull(c.execute(session, null));
     }
 
     private BundleContext createMockContext() throws ClassNotFoundException
diff --git a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/threadio/TestThreadIO.java b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/threadio/TestThreadIO.java
index 32ea917..2cc60df 100644
--- a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/threadio/TestThreadIO.java
+++ b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/threadio/TestThreadIO.java
@@ -38,6 +38,7 @@
     {
         ThreadIOImpl tio = new ThreadIOImpl();
         tio.start();
+
         List<ByteArrayOutputStream> list = new ArrayList<ByteArrayOutputStream>();
         for (int i = 0; i < 10; i++)
         {
@@ -66,6 +67,7 @@
     {
         ThreadIOImpl tio = new ThreadIOImpl();
         tio.start();
+
         System.out.println("Hello World");
         ByteArrayOutputStream out = new ByteArrayOutputStream();
         ByteArrayOutputStream err = new ByteArrayOutputStream();
diff --git a/gogo/shell/pom.xml b/gogo/shell/pom.xml
index 74e1097..da017c8 100644
--- a/gogo/shell/pom.xml
+++ b/gogo/shell/pom.xml
@@ -1,101 +1,94 @@
-<!--
- 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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-    <parent>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>gogo-parent</artifactId>
-        <version>0.6.0</version>
-        <relativePath>../gogo-parent/pom.xml</relativePath>
-    </parent>
-    <modelVersion>4.0.0</modelVersion>
-    <packaging>bundle</packaging>
-    <name>Apache Felix Gogo Shell</name>
-    <artifactId>org.apache.felix.gogo.shell</artifactId>
-    <version>0.13.0-SNAPSHOT</version>
-    <dependencies>
-        <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.core</artifactId>
-            <version>4.2.0</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.compendium</artifactId>
-            <version>4.0.0</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.felix</groupId>
-            <artifactId>org.apache.felix.gogo.runtime</artifactId>
-            <version>0.10.0</version>
-        </dependency>
-    </dependencies>
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.felix</groupId>
-                <artifactId>maven-bundle-plugin</artifactId>
-                <extensions>true</extensions>
-                <configuration>
-                    <instructions>
-                        <Export-Package>
-                        </Export-Package>
-                        <Import-Package>
-                            org.apache.felix.service.command; status="provisional",
-                            *
-                        </Import-Package>
-                        <Private-Package>
-			    org.apache.felix.gogo.shell,
-			    org.apache.felix.gogo.options
-			</Private-Package>
-                        <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
-                        <Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
-                        <Bundle-Activator>org.apache.felix.gogo.shell.Activator</Bundle-Activator>
-                        <Include-Resource>{maven-resources},META-INF/LICENSE=LICENSE,META-INF/NOTICE=NOTICE,META-INF/DEPENDENCIES=DEPENDENCIES</Include-Resource>
-                        <_versionpolicy>[$(version;==;$(@)),$(version;+;$(@)))</_versionpolicy>
-                        <_removeheaders>Private-Package,Ignore-Package,Include-Resource</_removeheaders>
-                    </instructions>
-                </configuration>
-            </plugin>
-            <plugin>
-                <groupId>org.codehaus.mojo</groupId>
-                <artifactId>rat-maven-plugin</artifactId>
-                <configuration>
-                    <excludeSubProjects>false</excludeSubProjects>
-                    <useEclipseDefaultExcludes>true</useEclipseDefaultExcludes>
-                    <useMavenDefaultExcludes>true</useMavenDefaultExcludes>
-                    <excludes>
-                        <param>doc/*</param>
-                        <param>maven-eclipse.xml</param>
-                        <param>.checkstyle</param>
-                        <param>.externalToolBuilders/*</param>
-                    </excludes>
-                </configuration>
-            </plugin>
-            <plugin>
-                <artifactId>maven-compiler-plugin</artifactId>
-                <configuration>
-                    <source>1.5</source>
-                    <target>1.5</target>
-                </configuration>
-            </plugin>
-        </plugins>
-    </build>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<parent>
+		<groupId>org.apache.felix</groupId>
+		<artifactId>gogo-parent</artifactId>
+		<version>0.6.0</version>
+		<relativePath>../gogo-parent/pom.xml</relativePath>
+	</parent>
+	<modelVersion>4.0.0</modelVersion>
+	<packaging>bundle</packaging>
+	<name>Apache Felix Gogo Shell</name>
+	<artifactId>org.apache.felix.gogo.shell</artifactId>
+	<version>0.13.0-SNAPSHOT</version>
+	<dependencies>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.core</artifactId>
+			<version>4.2.0</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.compendium</artifactId>
+			<version>4.0.0</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.felix</groupId>
+			<artifactId>org.apache.felix.gogo.runtime</artifactId>
+			<version>0.16.2</version>
+		</dependency>
+	</dependencies>
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.felix</groupId>
+				<artifactId>maven-bundle-plugin</artifactId>
+				<extensions>true</extensions>
+				<configuration>
+					<instructions>
+						<Export-Package>
+						</Export-Package>
+						<Import-Package>
+							org.apache.felix.service.command; status="provisional",
+							*
+						</Import-Package>
+						<Private-Package>
+							org.apache.felix.gogo.shell,
+							org.apache.felix.gogo.options
+						</Private-Package>
+						<Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
+						<Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
+						<Bundle-Activator>org.apache.felix.gogo.shell.Activator</Bundle-Activator>
+						<Include-Resource>{maven-resources},META-INF/LICENSE=LICENSE,META-INF/NOTICE=NOTICE,META-INF/DEPENDENCIES=DEPENDENCIES</Include-Resource>
+						<_versionpolicy>[$(version;==;$(@)),$(version;+;$(@)))</_versionpolicy>
+						<_removeheaders>Private-Package,Ignore-Package,Include-Resource</_removeheaders>
+					</instructions>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.codehaus.mojo</groupId>
+				<artifactId>rat-maven-plugin</artifactId>
+				<configuration>
+					<excludeSubProjects>false</excludeSubProjects>
+					<useEclipseDefaultExcludes>true</useEclipseDefaultExcludes>
+					<useMavenDefaultExcludes>true</useMavenDefaultExcludes>
+					<excludes>
+						<param>doc/*</param>
+						<param>maven-eclipse.xml</param>
+						<param>.checkstyle</param>
+						<param>.externalToolBuilders/*</param>
+					</excludes>
+				</configuration>
+			</plugin>
+			<plugin>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<configuration>
+					<source>1.5</source>
+					<target>1.5</target>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
 </project>
diff --git a/gogo/shell/src/main/java/org/apache/felix/gogo/options/Options.java b/gogo/shell/src/main/java/org/apache/felix/gogo/options/Options.java
index 570b067..194c32a 100644
--- a/gogo/shell/src/main/java/org/apache/felix/gogo/options/Options.java
+++ b/gogo/shell/src/main/java/org/apache/felix/gogo/options/Options.java
@@ -32,41 +32,47 @@
 /**
  * Yet another GNU long options parser. This one is configured by parsing its Usage string.
  */
-public class Options implements Option {
-    public static void main(String[] args) {
+public class Options implements Option
+{
+    public static void main(String[] args)
+    {
         final String[] usage = {
-            "test - test Options usage",
-            "  text before Usage: is displayed when usage() is called and no error has occurred.",
-            "  so can be used as a simple help message.",
-            "",
-            "Usage: testOptions [OPTION]... PATTERN [FILES]...",
-            "  Output control: arbitary non-option text can be included.",
-            "  -? --help                show help",
-            "  -c --count=COUNT           show COUNT lines",
-            "  -h --no-filename         suppress the prefixing filename on output",
-            "  -q --quiet, --silent     suppress all normal output",
-            "     --binary-files=TYPE   assume that binary files are TYPE",
-            "                           TYPE is 'binary', 'text', or 'without-match'",
-            "  -I                       equivalent to --binary-files=without-match",
-            "  -d --directories=ACTION  how to handle directories (default=skip)",
-            "                           ACTION is 'read', 'recurse', or 'skip'",
-            "  -D --devices=ACTION      how to handle devices, FIFOs and sockets",
-            "                           ACTION is 'read' or 'skip'",
-            "  -R, -r --recursive       equivalent to --directories=recurse" };
+                "test - test Options usage",
+                "  text before Usage: is displayed when usage() is called and no error has occurred.",
+                "  so can be used as a simple help message.",
+                "",
+                "Usage: testOptions [OPTION]... PATTERN [FILES]...",
+                "  Output control: arbitary non-option text can be included.",
+                "  -? --help                show help",
+                "  -c --count=COUNT         show COUNT lines",
+                "  -h --no-filename         suppress the prefixing filename on output",
+                "  -q --quiet, --silent     suppress all normal output",
+                "     --binary-files=TYPE   assume that binary files are TYPE",
+                "                           TYPE is 'binary', 'text', or 'without-match'",
+                "  -I                       equivalent to --binary-files=without-match",
+                "  -d --directories=ACTION  how to handle directories (default=skip)",
+                "                           ACTION is 'read', 'recurse', or 'skip'",
+                "  -D --devices=ACTION      how to handle devices, FIFOs and sockets",
+                "                           ACTION is 'read' or 'skip'",
+                "  -R, -r --recursive       equivalent to --directories=recurse" };
 
         Option opt = Options.compile(usage).parse(args);
 
-        if (opt.isSet("help")) {
+        if (opt.isSet("help"))
+        {
             opt.usage(); // includes text before Usage:
             return;
         }
 
-        if (opt.args().size() == 0)
+        if (opt.args().isEmpty())
+        {
             throw opt.usageError("PATTERN not specified");
-
+        }
         System.out.println(opt);
         if (opt.isSet("count"))
+        {
             System.out.println("count = " + opt.getNumber("count"));
+        }
         System.out.println("--directories specified: " + opt.isSet("directories"));
         System.out.println("directories=" + opt.get("directories"));
     }
@@ -75,10 +81,10 @@
 
     // Note: need to double \ within ""
     private static final String regex = "(?x)\\s*" + "(?:-([^-]))?" + // 1: short-opt-1
-            "(?:,?\\s*-(\\w))?" + // 2: short-opt-2
-            "(?:,?\\s*--(\\w[\\w-]*)(=\\w+)?)?" + // 3: long-opt-1 and 4:arg-1
-            "(?:,?\\s*--(\\w[\\w-]*))?" + // 5: long-opt-2
-            ".*?(?:\\(default=(.*)\\))?\\s*"; // 6: default
+        "(?:,?\\s*-(\\w))?" + // 2: short-opt-2
+        "(?:,?\\s*--(\\w[\\w-]*)(=\\w+)?)?" + // 3: long-opt-1 and 4:arg-1
+        "(?:,?\\s*--(\\w[\\w-]*))?" + // 5: long-opt-2
+        ".*?(?:\\(default=(.*)\\))?\\s*"; // 6: default
 
     private static final int GROUP_SHORT_OPT_1 = 1;
     private static final int GROUP_SHORT_OPT_2 = 2;
@@ -114,75 +120,96 @@
     private boolean optionsFirst = false;
     private boolean stopOnBadOption = false;
 
-    public static Option compile(String[] optSpec) {
+    public static Option compile(String[] optSpec)
+    {
         return new Options(optSpec, null, null);
     }
 
-    public static Option compile(String optSpec) {
+    public static Option compile(String optSpec)
+    {
         return compile(optSpec.split("\\n"));
     }
 
-    public static Option compile(String[] optSpec, Option gopt) {
+    public static Option compile(String[] optSpec, Option gopt)
+    {
         return new Options(optSpec, null, gopt);
     }
 
-    public static Option compile(String[] optSpec, String[] gspec) {
+    public static Option compile(String[] optSpec, String[] gspec)
+    {
         return new Options(optSpec, gspec, null);
     }
 
-    public Option setStopOnBadOption(boolean stopOnBadOption) {
+    public Option setStopOnBadOption(boolean stopOnBadOption)
+    {
         this.stopOnBadOption = stopOnBadOption;
         return this;
     }
 
-    public Option setOptionsFirst(boolean optionsFirst) {
+    public Option setOptionsFirst(boolean optionsFirst)
+    {
         this.optionsFirst = optionsFirst;
         return this;
     }
 
-    public boolean isSet(String name) {
+    public boolean isSet(String name)
+    {
         if (!optSet.containsKey(name))
+        {
             throw new IllegalArgumentException("option not defined in spec: " + name);
-
+        }
         return optSet.get(name);
     }
 
-    public Object getObject(String name) {
+    public Object getObject(String name)
+    {
         if (!optArg.containsKey(name))
+        {
             throw new IllegalArgumentException("option not defined with argument: " + name);
-
+        }
         List<Object> list = getObjectList(name);
 
         return list.isEmpty() ? "" : list.get(list.size() - 1);
     }
 
     @SuppressWarnings("unchecked")
-    public List<Object> getObjectList(String name) {
+    public List<Object> getObjectList(String name)
+    {
         List<Object> list;
         Object arg = optArg.get(name);
 
-        if ( arg == null ) {
+        if (arg == null)
+        {
             throw new IllegalArgumentException("option not defined with argument: " + name);
         }
-        
-        if (arg instanceof String) { // default value
+
+        if (arg instanceof String)
+        { // default value
             list = new ArrayList<Object>();
             if (!"".equals(arg))
+            {
                 list.add(arg);
+            }
         }
-        else {
+        else
+        {
             list = (List<Object>) arg;
         }
 
         return list;
     }
 
-    public List<String> getList(String name) {
+    public List<String> getList(String name)
+    {
         ArrayList<String> list = new ArrayList<String>();
-        for (Object o : getObjectList(name)) {
-            try {
+        for (Object o : getObjectList(name))
+        {
+            try
+            {
                 list.add((String) o);
-            } catch (ClassCastException e) {
+            }
+            catch (ClassCastException e)
+            {
                 throw new IllegalArgumentException("option not String: " + name);
             }
         }
@@ -190,72 +217,93 @@
     }
 
     @SuppressWarnings("unchecked")
-    private void addArg(String name, Object value) {
+    private void addArg(String name, Object value)
+    {
         List<Object> list;
         Object arg = optArg.get(name);
 
-        if (arg instanceof String) { // default value
+        if (arg instanceof String)
+        { // default value
             list = new ArrayList<Object>();
             optArg.put(name, list);
         }
-        else {
+        else
+        {
             list = (List<Object>) arg;
         }
 
         list.add(value);
     }
 
-    public String get(String name) {
-        try {
+    public String get(String name)
+    {
+        try
+        {
             return (String) getObject(name);
-        } catch (ClassCastException e) {
+        }
+        catch (ClassCastException e)
+        {
             throw new IllegalArgumentException("option not String: " + name);
         }
     }
 
-    public int getNumber(String name) {
+    public int getNumber(String name)
+    {
         String number = get(name);
-        try {
+        try
+        {
             if (number != null)
+            {
                 return Integer.parseInt(number);
+            }
             return 0;
-        } catch (NumberFormatException e) {
+        }
+        catch (NumberFormatException e)
+        {
             throw new IllegalArgumentException("option '" + name + "' not Number: " + number);
         }
     }
 
-    public List<Object> argObjects() {
+    public List<Object> argObjects()
+    {
         return xargs;
     }
 
-    public List<String> args() {
-        if (args == null) {
+    public List<String> args()
+    {
+        if (args == null)
+        {
             args = new ArrayList<String>();
-            for (Object arg : xargs) {
+            for (Object arg : xargs)
+            {
                 args.add(arg == null ? "null" : arg.toString());
             }
         }
         return args;
     }
 
-    public void usage() {
+    public void usage()
+    {
         StringBuilder buf = new StringBuilder();
         int index = 0;
 
-        if (error != null) {
+        if (error != null)
+        {
             buf.append(error);
             buf.append(NL);
             index = usageIndex;
         }
 
-        for (int i = index; i < spec.length; ++i) {
+        for (int i = index; i < spec.length; ++i)
+        {
             buf.append(spec[i]);
             buf.append(NL);
         }
 
         String msg = buf.toString();
 
-        if (errStream != null) {
+        if (errStream != null)
+        {
             errStream.print(msg);
         }
     }
@@ -263,21 +311,25 @@
     /**
      * prints usage message and returns IllegalArgumentException, for you to throw.
      */
-    public IllegalArgumentException usageError(String s) {
+    public IllegalArgumentException usageError(String s)
+    {
         error = usageName + ": " + s;
         usage();
         return new IllegalArgumentException(error);
     }
 
     // internal constructor
-    private Options(String[] spec, String[] gspec, Option opt) {
+    private Options(String[] spec, String[] gspec, Option opt)
+    {
         this.gspec = gspec;
         Options gopt = (Options) opt;
 
-        if (gspec == null && gopt == null) {
+        if (gspec == null && gopt == null)
+        {
             this.spec = spec;
         }
-        else {
+        else
+        {
             ArrayList<String> list = new ArrayList<String>();
             list.addAll(Arrays.asList(spec));
             list.addAll(Arrays.asList(gspec != null ? gspec : gopt.gspec));
@@ -289,15 +341,22 @@
 
         parseSpec(myOptSet, myOptArg);
 
-        if (gopt != null) {
-            for (Entry<String, Boolean> e : gopt.optSet.entrySet()) {
+        if (gopt != null)
+        {
+            for (Entry<String, Boolean> e : gopt.optSet.entrySet())
+            {
                 if (e.getValue())
+                {
                     myOptSet.put(e.getKey(), true);
+                }
             }
 
-            for (Entry<String, Object> e : gopt.optArg.entrySet()) {
-                if (!e.getValue().equals(""))
+            for (Entry<String, Object> e : gopt.optArg.entrySet())
+            {
+                if (!"".equals(e.getValue()))
+                {
                     myOptArg.put(e.getKey(), e.getValue());
+                }
             }
 
             gopt.reset();
@@ -313,18 +372,24 @@
     /**
      * parse option spec.
      */
-    private void parseSpec(Map<String, Boolean> myOptSet, Map<String, Object> myOptArg) {
+    private void parseSpec(Map<String, Boolean> myOptSet, Map<String, Object> myOptArg)
+    {
         int index = 0;
-        for (String line : spec) {
+        for (String line : spec)
+        {
             Matcher m = parser.matcher(line);
 
-            if (m.matches()) {
+            if (m.matches())
+            {
                 final String opt = m.group(GROUP_LONG_OPT_1);
                 final String name = (opt != null) ? opt : m.group(GROUP_SHORT_OPT_1);
 
-                if (name != null) {
+                if (name != null)
+                {
                     if (myOptSet.containsKey(name))
+                    {
                         throw new IllegalArgumentException("duplicate option in spec: --" + name);
+                    }
                     myOptSet.put(name, false);
                 }
 
@@ -333,26 +398,35 @@
                     myOptArg.put(opt, dflt);
 
                 String opt2 = m.group(GROUP_LONG_OPT_2);
-                if (opt2 != null) {
+                if (opt2 != null)
+                {
                     optAlias.put(opt2, opt);
                     myOptSet.put(opt2, false);
                     if (m.group(GROUP_ARG_1) != null)
+                    {
                         myOptArg.put(opt2, "");
+                    }
                 }
 
-                for (int i = 0; i < 2; ++i) {
+                for (int i = 0; i < 2; ++i)
+                {
                     String sopt = m.group(i == 0 ? GROUP_SHORT_OPT_1 : GROUP_SHORT_OPT_2);
-                    if (sopt != null) {
+                    if (sopt != null)
+                    {
                         if (optName.containsKey(sopt))
+                        {
                             throw new IllegalArgumentException("duplicate option in spec: -" + sopt);
+                        }
                         optName.put(sopt, name);
                     }
                 }
             }
 
-            if (usageName == UNKNOWN) {
+            if (usageName == UNKNOWN)
+            {
                 Matcher u = uname.matcher(line);
-                if (u.find()) {
+                if (u.find())
+                {
                     usageName = u.group(1);
                     usageIndex = index;
                 }
@@ -362,7 +436,8 @@
         }
     }
 
-    private void reset() {
+    private void reset()
+    {
         optSet.clear();
         optSet.putAll(unmodifiableOptSet);
         optArg.clear();
@@ -372,32 +447,40 @@
         error = null;
     }
 
-    public Option parse(Object[] argv) {
+    public Option parse(Object[] argv)
+    {
         return parse(argv, false);
     }
 
-    public Option parse(List<? extends Object> argv) {
+    public Option parse(List<? extends Object> argv)
+    {
         return parse(argv, false);
     }
 
-    public Option parse(Object[] argv, boolean skipArg0) {
+    public Option parse(Object[] argv, boolean skipArg0)
+    {
         if (null == argv)
+        {
             throw new IllegalArgumentException("argv is null");
-        
+        }
         return parse(Arrays.asList(argv), skipArg0);
     }
 
-    public Option parse(List<? extends Object> argv, boolean skipArg0) {
+    public Option parse(List<? extends Object> argv, boolean skipArg0)
+    {
         reset();
         List<Object> args = new ArrayList<Object>();
         args.addAll(Arrays.asList(defArgs));
 
-        for (Object arg : argv) {
-            if (skipArg0) {
+        for (Object arg : argv)
+        {
+            if (skipArg0)
+            {
                 skipArg0 = false;
                 usageName = arg.toString();
             }
-            else {
+            else
+            {
                 args.add(arg);
             }
         }
@@ -406,89 +489,114 @@
         String needOpt = null;
         boolean endOpt = false;
 
-        for (Object oarg : args) {
+        for (Object oarg : args)
+        {
             String arg = oarg == null ? "null" : oarg.toString();
 
-            if (endOpt) {
+            if (endOpt)
+            {
                 xargs.add(oarg);
             }
-            else if (needArg != null) {
+            else if (needArg != null)
+            {
                 addArg(needArg, oarg);
                 needArg = null;
                 needOpt = null;
             }
-            else if (!arg.startsWith("-") || "-".equals(oarg)) {
+            else if (!arg.startsWith("-") || "-".equals(oarg))
+            {
                 if (optionsFirst)
+                {
                     endOpt = true;
+                }
                 xargs.add(oarg);
             }
-            else {
+            else
+            {
                 if (arg.equals("--"))
+                {
                     endOpt = true;
-                else if (arg.startsWith("--")) {
+                }
+                else if (arg.startsWith("--"))
+                {
                     int eq = arg.indexOf("=");
                     String value = (eq == -1) ? null : arg.substring(eq + 1);
                     String name = arg.substring(2, ((eq == -1) ? arg.length() : eq));
                     List<String> names = new ArrayList<String>();
 
-                    if (optSet.containsKey(name)) {
+                    if (optSet.containsKey(name))
+                    {
                         names.add(name);
                     }
-                    else {
-                        for (String k : optSet.keySet()) {
+                    else
+                    {
+                        for (String k : optSet.keySet())
+                        {
                             if (k.startsWith(name))
                                 names.add(k);
                         }
                     }
 
-                    switch (names.size()) {
-                    case 1:
-                        name = names.get(0);
-                        optSet.put(name, true);
-                        if (optArg.containsKey(name)) {
-                            if (value != null)
-                                addArg(name, value);
-                            else
-                                needArg = name;
-                        }
-                        else if (value != null) {
-                            throw usageError("option '--" + name + "' doesn't allow an argument");
-                        }
-                        break;
-
-                    case 0:
-                        if (stopOnBadOption) {
-                            endOpt = true;
-                            xargs.add(oarg);
+                    switch (names.size())
+                    {
+                        case 1:
+                            name = names.get(0);
+                            optSet.put(name, true);
+                            if (optArg.containsKey(name))
+                            {
+                                if (value != null)
+                                    addArg(name, value);
+                                else
+                                    needArg = name;
+                            }
+                            else if (value != null)
+                            {
+                                throw usageError("option '--" + name + "' doesn't allow an argument");
+                            }
                             break;
-                        }
-                        else
-                            throw usageError("invalid option '--" + name + "'");
 
-                    default:
-                        throw usageError("option '--" + name + "' is ambiguous: " + names);
+                        case 0:
+                            if (stopOnBadOption)
+                            {
+                                endOpt = true;
+                                xargs.add(oarg);
+                                break;
+                            }
+                            else
+                                throw usageError("invalid option '--" + name + "'");
+
+                        default:
+                            throw usageError("option '--" + name + "' is ambiguous: " + names);
                     }
                 }
-                else {
-                    for (int i = 1; i < arg.length(); i++) {
+                else
+                {
+                    for (int i = 1; i < arg.length(); i++)
+                    {
                         String c = String.valueOf(arg.charAt(i));
-                        if (optName.containsKey(c)) {
+                        if (optName.containsKey(c))
+                        {
                             String name = optName.get(c);
                             optSet.put(name, true);
-                            if (optArg.containsKey(name)) {
+                            if (optArg.containsKey(name))
+                            {
                                 int k = i + 1;
-                                if (k < arg.length()) {
+                                if (k < arg.length())
+                                {
                                     addArg(name, arg.substring(k));
                                 }
-                                else {
+                                else
+                                {
                                     needOpt = c;
                                     needArg = name;
                                 }
                                 break;
                             }
                         }
-                        else {
-                            if (stopOnBadOption) {
+                        else
+                        {
+                            if (stopOnBadOption)
+                            {
                                 xargs.add("-" + c);
                                 endOpt = true;
                             }
@@ -500,14 +608,17 @@
             }
         }
 
-        if (needArg != null) {
+        if (needArg != null)
+        {
             String name = (needOpt != null) ? needOpt : "--" + needArg;
             throw usageError("option '" + name + "' requires an argument");
         }
 
         // remove long option aliases
-        for (Entry<String, String> alias : optAlias.entrySet()) {
-            if (optSet.get(alias.getKey())) {
+        for (Entry<String, String> alias : optAlias.entrySet())
+        {
+            if (optSet.get(alias.getKey()))
+            {
                 optSet.put(alias.getValue(), true);
                 if (optArg.containsKey(alias.getKey()))
                     optArg.put(alias.getValue(), optArg.get(alias.getKey()));
@@ -520,7 +631,8 @@
     }
 
     @Override
-    public String toString() {
+    public String toString()
+    {
         return "isSet" + optSet + "\nArg" + optArg + "\nargs" + xargs;
     }
 
diff --git a/gogo/shell/src/main/java/org/apache/felix/gogo/shell/Activator.java b/gogo/shell/src/main/java/org/apache/felix/gogo/shell/Activator.java
index 666dfc1..153e7e2 100644
--- a/gogo/shell/src/main/java/org/apache/felix/gogo/shell/Activator.java
+++ b/gogo/shell/src/main/java/org/apache/felix/gogo/shell/Activator.java
@@ -18,10 +18,11 @@
  */
 package org.apache.felix.gogo.shell;
 
+import java.util.Arrays;
 import java.util.Dictionary;
 import java.util.HashSet;
 import java.util.Hashtable;
-import java.util.Iterator;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -43,7 +44,8 @@
     private ServiceTracker commandProcessorTracker;
     private Set<ServiceRegistration> regs;
 
-    private ExecutorService executor;
+    private volatile ExecutorService executor;
+    private volatile StartShellJob shellJob;
 
     public Activator()
     {
@@ -59,17 +61,21 @@
 
     public void stop(BundleContext context) throws Exception
     {
-        Iterator<ServiceRegistration> iterator = regs.iterator();
-        while (iterator.hasNext())
+        Set<ServiceRegistration> currentRegs = new HashSet<ServiceRegistration>();
+        synchronized (regs)
         {
-            ServiceRegistration reg = iterator.next();
-            reg.unregister();
-            iterator.remove();
+            currentRegs.addAll(regs);
+            regs.clear();
         }
 
-        stopShell();
+        for (ServiceRegistration reg : currentRegs)
+        {
+            reg.unregister();
+        }
 
         this.commandProcessorTracker.close();
+
+        stopShell();
     }
 
     private ServiceTracker createCommandProcessorTracker()
@@ -98,26 +104,34 @@
         Dictionary<String, Object> dict = new Hashtable<String, Object>();
         dict.put(CommandProcessor.COMMAND_SCOPE, "gogo");
 
+        Set<ServiceRegistration> currentRegs = new HashSet<ServiceRegistration>();
+
         // register converters
-        regs.add(context.registerService(Converter.class.getName(), new Converters(context.getBundle(0).getBundleContext()), null));
+        currentRegs.add(context.registerService(Converter.class.getName(), new Converters(context.getBundle(0).getBundleContext()), null));
 
         // register commands
 
         dict.put(CommandProcessor.COMMAND_FUNCTION, Builtin.functions);
-        regs.add(context.registerService(Builtin.class.getName(), new Builtin(), dict));
+        currentRegs.add(context.registerService(Builtin.class.getName(), new Builtin(), dict));
 
         dict.put(CommandProcessor.COMMAND_FUNCTION, Procedural.functions);
-        regs.add(context.registerService(Procedural.class.getName(), new Procedural(), dict));
+        currentRegs.add(context.registerService(Procedural.class.getName(), new Procedural(), dict));
 
         dict.put(CommandProcessor.COMMAND_FUNCTION, Posix.functions);
-        regs.add(context.registerService(Posix.class.getName(), new Posix(), dict));
+        currentRegs.add(context.registerService(Posix.class.getName(), new Posix(), dict));
 
         dict.put(CommandProcessor.COMMAND_FUNCTION, Telnet.functions);
-        regs.add(context.registerService(Telnet.class.getName(), new Telnet(processor), dict));
+        currentRegs.add(context.registerService(Telnet.class.getName(), new Telnet(processor), dict));
 
         Shell shell = new Shell(context, processor);
         dict.put(CommandProcessor.COMMAND_FUNCTION, Shell.functions);
-        regs.add(context.registerService(Shell.class.getName(), shell, dict));
+        currentRegs.add(context.registerService(Shell.class.getName(), shell, dict));
+
+        synchronized (regs)
+        {
+            regs.addAll(currentRegs);
+            currentRegs.clear();
+        }
 
         // start shell on a separate thread...
         executor = Executors.newSingleThreadExecutor(new ThreadFactory()
@@ -127,20 +141,31 @@
                 return new Thread(runnable, "Gogo shell");
             }
         });
-        executor.submit(new StartShellJob(context, processor));
+        shellJob = new StartShellJob(context, processor);
+        executor.submit(shellJob);
     }
 
     private void stopShell()
     {
         if (executor != null && !(executor.isShutdown() || executor.isTerminated()))
         {
-            executor.shutdownNow();
+            if (shellJob != null)
+            {
+                shellJob.terminate();
+            }
+            executor.shutdown();
 
             try
             {
                 if (!executor.awaitTermination(5, TimeUnit.SECONDS))
                 {
                     System.err.println("!!! FAILED TO STOP EXECUTOR !!!");
+                    Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces();
+                    for (Map.Entry<Thread, StackTraceElement[]> entry : allStackTraces.entrySet())
+                    {
+                        Thread t = entry.getKey();
+                        System.err.printf("Thread: %s (%s): %s\n", t.getName(), t.getState(), Arrays.toString(entry.getValue()));
+                    }
                 }
             }
             catch (InterruptedException e)
@@ -156,6 +181,7 @@
     {
         private final BundleContext context;
         private final CommandProcessor processor;
+        private volatile CommandSession session;
 
         public StartShellJob(BundleContext context, CommandProcessor processor)
         {
@@ -165,7 +191,7 @@
 
         public void run()
         {
-            CommandSession session = processor.createSession(System.in, System.out, System.err);
+            session = processor.createSession(System.in, System.out, System.err);
             try
             {
                 // wait for gosh command to be registered
@@ -178,6 +204,11 @@
                 args = (args == null) ? "" : args;
                 session.execute("gosh --login " + args);
             }
+            catch (InterruptedException e)
+            {
+                // Ok, back off...
+                Thread.currentThread().interrupt();
+            }
             catch (Exception e)
             {
                 Object loc = session.get(".location");
@@ -191,8 +222,18 @@
             }
             finally
             {
-                session.close();
+                terminate();
             }
         }
+
+        public void terminate()
+        {
+            if (session != null)
+            {
+                session.close();
+                session = null;
+            }
+            Thread.currentThread().interrupt();
+        }
     }
 }
\ No newline at end of file
diff --git a/gogo/shell/src/main/java/org/apache/felix/gogo/shell/Console.java b/gogo/shell/src/main/java/org/apache/felix/gogo/shell/Console.java
index ee21598..efba820 100644
--- a/gogo/shell/src/main/java/org/apache/felix/gogo/shell/Console.java
+++ b/gogo/shell/src/main/java/org/apache/felix/gogo/shell/Console.java
@@ -32,7 +32,7 @@
     private final InputStream in;
     private final PrintStream out;
     private final History history;
-    private boolean quit;
+    private volatile boolean quit;
 
     public Console(CommandSession session, History history)
     {
@@ -46,7 +46,7 @@
     {
         try
         {
-            while (!quit)
+            while (!Thread.currentThread().isInterrupted() && !quit)
             {
                 CharSequence line = getLine(getPrompt());
 
@@ -95,8 +95,7 @@
                             loc = "gogo";
                         }
 
-                        out.println(loc + ": " + e.getClass().getSimpleName() + ": "
-                            + e.getMessage());
+                        out.println(loc + ": " + e.getClass().getSimpleName() + ": " + e.getMessage());
                     }
                 }
                 finally
@@ -146,7 +145,20 @@
         while (!quit)
         {
             out.flush();
-            int c = in.read();
+
+            int c = 0;
+            try
+            {
+                c = in.read();
+            }
+            catch (IOException e)
+            {
+                if ("Stream closed".equals(e.getMessage())) {
+                    quit = true;
+                } else {
+                    throw e;
+                }
+            }
 
             switch (c)
             {
diff --git a/gogo/shell/src/main/java/org/apache/felix/gogo/shell/Converters.java b/gogo/shell/src/main/java/org/apache/felix/gogo/shell/Converters.java
index b76440a..ad189cb 100644
--- a/gogo/shell/src/main/java/org/apache/felix/gogo/shell/Converters.java
+++ b/gogo/shell/src/main/java/org/apache/felix/gogo/shell/Converters.java
@@ -24,19 +24,19 @@
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.util.Arrays;
-import java.util.Formatter;
 
+import org.apache.felix.service.command.Converter;
+import org.apache.felix.service.command.Function;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
-import org.apache.felix.service.command.Converter;
-import org.apache.felix.service.command.Function;
 import org.osgi.service.startlevel.StartLevel;
 
 public class Converters implements Converter
 {
     private final BundleContext context;
+
     public Converters(BundleContext context)
     {
         this.context = context;
@@ -67,9 +67,6 @@
 
     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)
@@ -77,10 +74,9 @@
             spid = pid.toString();
         }
 
-        f.format("%06d %3s %-40s %s", ref.getProperty("service.id"),
+        return String.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)
diff --git a/gogo/shell/src/main/java/org/apache/felix/gogo/shell/Shell.java b/gogo/shell/src/main/java/org/apache/felix/gogo/shell/Shell.java
index 895b577..a2c6602 100644
--- a/gogo/shell/src/main/java/org/apache/felix/gogo/shell/Shell.java
+++ b/gogo/shell/src/main/java/org/apache/felix/gogo/shell/Shell.java
@@ -35,6 +35,7 @@
 import org.apache.felix.gogo.options.Options;
 import org.apache.felix.service.command.CommandProcessor;
 import org.apache.felix.service.command.CommandSession;
+import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleException;
 
@@ -49,6 +50,8 @@
     private final CommandProcessor processor;
     private final History history;
 
+    private volatile Bundle systemBundle;
+
     public Shell(BundleContext context, CommandProcessor processor)
     {
         this.context = context;
@@ -79,6 +82,9 @@
         boolean login = opt.isSet("login");
         boolean interactive = !opt.isSet("nointeractive");
 
+        // We grab this bundle as early as possible to avoid having to deal with invalid bundleContexts of this bundle during shutdowns...
+        systemBundle = context.getBundle(0);
+
         if (opt.isSet("help"))
         {
             opt.usage();
@@ -94,8 +100,7 @@
             throw opt.usageError("option --command requires argument(s)");
         }
 
-        CommandSession newSession = (login ? session : processor.createSession(
-            session.getKeyboard(), session.getConsole(), System.err));
+        CommandSession newSession = (login ? session : processor.createSession(session.getKeyboard(), session.getConsole(), System.err));
 
         if (opt.isSet("xtrace"))
         {
@@ -108,7 +113,8 @@
             if (!new File(uri).exists())
             {
                 URL url = getClass().getResource("/ext/gosh_profile");
-                if (url == null) {
+                if (url == null)
+                {
                     url = getClass().getResource("/gosh_profile");
                 }
                 uri = (url == null) ? null : url.toURI();
@@ -173,10 +179,17 @@
             result = newSession.execute(program);
         }
 
-        if (login && interactive && !opt.isSet("noshutdown"))
+        if (login && interactive)
         {
-            System.out.println("gosh: stopping framework");
-            shutdown();
+            if (opt.isSet("noshutdown"))
+            {
+                System.out.println("gosh: stopping shell");
+            }
+            else
+            {
+                System.out.println("gosh: stopping shell and framework");
+                shutdown();
+            }
         }
 
         return result;
@@ -189,7 +202,11 @@
 
     private void shutdown() throws BundleException
     {
-        context.getBundle(0).stop();
+        if (systemBundle != null)
+        {
+            systemBundle.stop();
+            systemBundle = null;
+        }
     }
 
     public Object source(CommandSession session, String script) throws Exception
@@ -258,10 +275,12 @@
         }
     }
 
-    public String[] history() {
+    public String[] history()
+    {
         Iterator<String> history = this.history.getHistory();
         List<String> lines = new ArrayList<String>();
-        for (int i = 1; history.hasNext(); i++) {
+        for (int i = 1; history.hasNext(); i++)
+        {
             lines.add(String.format("%5d  %s", i, history.next()));
         }
         return lines.toArray(new String[lines.size()]);