Move ServiceMix Kernel trunk into Felix

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@768912 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/karaf/gshell/gshell-admin/pom.xml b/karaf/gshell/gshell-admin/pom.xml
new file mode 100644
index 0000000..0a6a2a4
--- /dev/null
+++ b/karaf/gshell/gshell-admin/pom.xml
@@ -0,0 +1,154 @@
+<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">
+
+    <!--
+
+        Licensed to the Apache Software Foundation (ASF) under one or more
+        contributor license agreements.  See the NOTICE file distributed with
+        this work for additional information regarding copyright ownership.
+        The ASF licenses this file to You under the Apache License, Version 2.0
+        (the "License"); you may not use this file except in compliance with
+        the License.  You may obtain a copy of the License at
+
+           http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.servicemix.kernel.gshell</groupId>
+        <artifactId>gshell</artifactId>
+        <version>1.2.0-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.apache.servicemix.kernel.gshell</groupId>
+    <artifactId>org.apache.servicemix.kernel.gshell.admin</artifactId>
+    <packaging>bundle</packaging>
+    <version>1.2.0-SNAPSHOT</version>
+    <name>Apache ServiceMix Kernel :: GShell Admin</name>
+
+    <description>
+        Provides administration commands
+    </description>
+
+    <properties>
+        <gshell.osgi.import>
+            org.apache.servicemix.kernel.main.spi.*;resolution:=optional,
+            org.apache.geronimo.gshell*,
+        </gshell.osgi.import>
+        <gshell.osgi.export>
+            org.apache.servicemix.jpm
+        </gshell.osgi.export>
+        <gshell.osgi.private>
+            org.apache.servicemix.kernel.gshell.admin.*,
+            org.apache.servicemix.jpm.impl
+        </gshell.osgi.private>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.servicemix.kernel.gshell</groupId>
+            <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.osgi</groupId>
+            <artifactId>spring-osgi-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>${pom.basedir}/src/main/resources</directory>
+                <includes>
+                    <include>**/*</include>
+                </includes>
+            </resource>
+            <resource>
+                <directory>${pom.basedir}/src/main/filtered-resources</directory>
+                <filtering>true</filtering>
+                <includes>
+                    <include>**/*</include>
+                </includes>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-resources-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>copy-resources</id>
+                        <!-- here the phase you need -->
+                        <phase>compile</phase>
+                        <goals>
+                            <goal>copy-resources</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>${basedir}/target/classes/org/apache/servicemix/kernel/gshell/admin/etc</outputDirectory>
+                            <resources>          
+                                <resource>
+                                    <directory>../../${config.location}</directory>
+                                    <includes>
+                                        <include>config.properties</include>
+                                    </includes>
+                                </resource>
+                            </resources>              
+                        </configuration>            
+                    </execution>
+                </executions>
+            </plugin>            
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>Main</mainClass>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+                        <Export-Package>org.apache.servicemix.kernel.gshell.admin.*;version=${project.version}
+                        </Export-Package>
+                        <Import-Package>
+                            org.apache.geronimo.gshell.wisdom.command,
+                            org.apache.geronimo.gshell.wisdom.registry,
+                            org.apache.servicemix.kernel.gshell.core,
+                            *
+                        </Import-Package>
+                        <Private-Package>org.apache.servicemix.jpm.*</Private-Package>
+                        <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/karaf/gshell/gshell-admin/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/admin/etc/org.apache.servicemix.features.cfg b/karaf/gshell/gshell-admin/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/admin/etc/org.apache.servicemix.features.cfg
new file mode 100644
index 0000000..2e22aec
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/admin/etc/org.apache.servicemix.features.cfg
@@ -0,0 +1,28 @@
+################################################################################

+#

+#    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.

+#

+################################################################################

+

+#

+# Comma separated list of features repositories to register by default

+#

+featuresRepositories=mvn:org.apache.servicemix.kernel/apache-servicemix-kernel/${version}/xml/features

+

+#

+# Comma separated list of features to install at startup

+#

+featuresBoot=

diff --git a/karaf/gshell/gshell-admin/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/admin/etc/startup.properties b/karaf/gshell/gshell-admin/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/admin/etc/startup.properties
new file mode 100644
index 0000000..f9689b1
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/admin/etc/startup.properties
@@ -0,0 +1,76 @@
+################################################################################
+#
+#    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.
+#
+################################################################################
+
+# This file allows you to control the start level of each bundle.
+#
+
+#
+# Startup core services like logging
+#
+org/ops4j/pax/url/pax-url-mvn/${pax.url.version}/pax-url-mvn-${pax.url.version}.jar=5
+org/ops4j/pax/url/pax-url-wrap/${pax.url.version}/pax-url-wrap-${pax.url.version}.jar=5
+org/apache/geronimo/specs/geronimo-servlet_2.5_spec/${geronimo.servlet.version}/geronimo-servlet_2.5_spec-${geronimo.servlet.version}.jar=10
+org/apache/servicemix/specs/org.apache.servicemix.specs.jaxp-api-1.4/${servicemix.specs.version}/org.apache.servicemix.specs.jaxp-api-1.4-${servicemix.specs.version}.jar=10
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.jaxp-ri/${jaxp.ri.version}/org.apache.servicemix.bundles.jaxp-ri-${jaxp.ri.version}.jar=10
+org/apache/felix/org.osgi.compendium/${felix.compendium.version}/org.osgi.compendium-${felix.compendium.version}.jar=10
+org/apache/felix/org.apache.felix.configadmin/${felix.configadmin.version}/org.apache.felix.configadmin-${felix.configadmin.version}.jar=10
+org/apache/geronimo/specs/geronimo-annotation_1.0_spec/${geronimo.annotation.version}/geronimo-annotation_1.0_spec-${geronimo.annotation.version}.jar=10
+org/apache/felix/org.apache.felix.prefs/${felix.prefs.version}/org.apache.felix.prefs-${felix.prefs.version}.jar=10
+org/apache/servicemix/kernel/org.apache.servicemix.kernel.filemonitor/${pom.version}/org.apache.servicemix.kernel.filemonitor-${pom.version}.jar=15
+org/ops4j/pax/logging/pax-logging-api/${pax.logging.version}/pax-logging-api-${pax.logging.version}.jar=20
+org/ops4j/pax/logging/pax-logging-service/${pax.logging.version}/pax-logging-service-${pax.logging.version}.jar=20
+
+#
+# The rest of the services..
+#
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.jline/${jline.version}/org.apache.servicemix.bundles.jline-${jline.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.aopalliance/${aopalliance.version}/org.apache.servicemix.bundles.aopalliance-${aopalliance.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.cglib/${cglib.version}/org.apache.servicemix.bundles.cglib-${cglib.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.mina/${mina.version}/org.apache.servicemix.bundles.mina-${mina.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.oro/${oro.version}/org.apache.servicemix.bundles.oro-${oro.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.commons-codec/${commons.codec.version}/org.apache.servicemix.bundles.commons-codec-${commons.codec.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.commons-httpclient/${commons.httpclient.version}/org.apache.servicemix.bundles.commons-httpclient-${commons.httpclient.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.commons-jexl/${commons.jexl.version}/org.apache.servicemix.bundles.commons-jexl-${commons.jexl.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.commons-vfs/${commons.vfs.version}/org.apache.servicemix.bundles.commons-vfs-${commons.vfs.version}.jar=30
+org/springframework/spring-aop/${spring.version}/spring-aop-${spring.version}.jar=30
+org/springframework/spring-beans/${spring.version}/spring-beans-${spring.version}.jar=30
+org/springframework/spring-context/${spring.version}/spring-context-${spring.version}.jar=30
+org/springframework/spring-core/${spring.version}/spring-core-${spring.version}.jar=30
+org/springframework/osgi/spring-osgi-core/${spring.osgi.version}/spring-osgi-core-${spring.osgi.version}.jar=30
+org/springframework/osgi/spring-osgi-extender/${spring.osgi.version}/spring-osgi-extender-${spring.osgi.version}.jar=30
+org/springframework/osgi/spring-osgi-io/${spring.osgi.version}/spring-osgi-io-${spring.osgi.version}.jar=30
+org/apache/servicemix/kernel/org.apache.servicemix.kernel.spring/${pom.version}/org.apache.servicemix.kernel.spring-${pom.version}.jar=30
+org/apache/servicemix/kernel/org.apache.servicemix.kernel.management/${pom.version}/org.apache.servicemix.kernel.management-${pom.version}.jar=30
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.admin/${pom.version}/org.apache.servicemix.kernel.gshell.admin-${pom.version}.jar=30
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.osgi/${pom.version}/org.apache.servicemix.kernel.gshell.osgi-${pom.version}.jar=30
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.features/${pom.version}/org.apache.servicemix.kernel.gshell.features-${pom.version}.jar=30
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.log/${pom.version}/org.apache.servicemix.kernel.gshell.log-${pom.version}.jar=30
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.config/${pom.version}/org.apache.servicemix.kernel.gshell.config-${pom.version}.jar=30
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.packages/${pom.version}/org.apache.servicemix.kernel.gshell.packages-${pom.version}.jar=30
+org/apache/servicemix/kernel/jaas/org.apache.servicemix.kernel.jaas.config/${pom.version}/org.apache.servicemix.kernel.jaas.config-${pom.version}.jar=30
+org/apache/servicemix/kernel/jaas/org.apache.servicemix.kernel.jaas.modules/${pom.version}/org.apache.servicemix.kernel.jaas.modules-${pom.version}.jar=30
+com/google/code/sshd/sshd/${sshd.version}/sshd-${sshd.version}.jar=30
+org/osgi/jmx/${osgi.jmx.version}/jmx-${osgi.jmx.version}.jar=30
+com/oracle/osgi/jmx-impl/${osgi.jmx.version}/jmx-impl-${osgi.jmx.version}.jar=30
+
+#
+# Start console last
+#
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.core/${pom.version}/org.apache.servicemix.kernel.gshell.core-${pom.version}.jar=40
+
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/Process.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/Process.java
new file mode 100644
index 0000000..eedb31d
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/Process.java
@@ -0,0 +1,47 @@
+/*
+ * 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.servicemix.jpm;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+/**
+ * Interface representing a process
+ */
+public interface Process extends Serializable {
+
+    /**
+     * Retrieves the PID of the process
+     * @return the pid
+     */
+    int getPid();
+
+    /**
+     * Check if this process is still running
+     * @return <code>true</code> if the process is running
+     * @throws IOException if an error occurs
+     */
+    boolean isRunning() throws IOException;
+
+    /**
+     * Destroy the process.
+     *
+     * @throws IOException
+     */
+    void destroy() throws IOException;
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/ProcessBuilder.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/ProcessBuilder.java
new file mode 100644
index 0000000..6862222
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/ProcessBuilder.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.jpm;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Interface used to create new processes.
+ */
+public interface ProcessBuilder {
+
+    /**
+     * Specified the current directory to run the command from
+     *
+     * @param dir the directory to run the command from
+     * @return the ProcessBuilder instance
+     */
+    ProcessBuilder directory(File dir);
+
+    /**
+     * Set the command to execute
+     *
+     * @param command the command to execute
+     * @return the ProcessBuilder instance
+     */
+    ProcessBuilder command(String command);
+
+    /**
+     * Create and start the process
+     *
+     * @return the process that has been started
+     * @throws IOException if the process can not be created
+     */
+    Process start() throws IOException;
+
+    /**
+     * Attach to an existing process
+     *
+     * @return the process that has been attached
+     * @throws IOException if the process can not be attached to
+     */
+    Process attach(int pid) throws IOException;
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/ProcessBuilderFactory.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/ProcessBuilderFactory.java
new file mode 100644
index 0000000..433b615
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/ProcessBuilderFactory.java
@@ -0,0 +1,30 @@
+/*
+ * 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.servicemix.jpm;
+
+/**
+ * Factory for process builders.
+ */
+public abstract class ProcessBuilderFactory {
+
+    public static ProcessBuilderFactory newInstance() {
+        return new org.apache.servicemix.jpm.impl.ProcessBuilderFactoryImpl();
+    }
+
+    public abstract ProcessBuilder newBuilder();
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessBuilderFactoryImpl.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessBuilderFactoryImpl.java
new file mode 100644
index 0000000..4db4657
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessBuilderFactoryImpl.java
@@ -0,0 +1,27 @@
+/*
+ * 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.servicemix.jpm.impl;
+
+import org.apache.servicemix.jpm.ProcessBuilder;
+import org.apache.servicemix.jpm.ProcessBuilderFactory;
+
+public class ProcessBuilderFactoryImpl extends ProcessBuilderFactory {
+
+    public ProcessBuilder newBuilder() {
+        return new ProcessBuilderImpl();
+    }
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessBuilderImpl.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessBuilderImpl.java
new file mode 100644
index 0000000..649da6b
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessBuilderImpl.java
@@ -0,0 +1,48 @@
+/*
+ * 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.servicemix.jpm.impl;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.servicemix.jpm.Process;
+import org.apache.servicemix.jpm.ProcessBuilder;
+
+
+public class ProcessBuilderImpl implements ProcessBuilder {
+
+    private File dir;
+    private String command;
+
+    public ProcessBuilder directory(File dir) {
+        this.dir = dir;
+        return this;
+    }
+
+    public ProcessBuilder command(String command) {
+        this.command = command;
+        return this;
+    }
+
+    public Process start() throws IOException {
+        return ProcessImpl.create(dir, command);
+    }
+
+    public Process attach(int pid) throws IOException {
+        return ProcessImpl.attach(pid);
+    }
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessImpl.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessImpl.java
new file mode 100644
index 0000000..71146fa
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessImpl.java
@@ -0,0 +1,150 @@
+/*
+ * 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.servicemix.jpm.impl;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.InterruptedIOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.servicemix.jpm.Process;
+
+public class ProcessImpl implements Process {
+
+    private int pid;
+    //private File input;
+    //private File output;
+    //private File error;
+
+    public ProcessImpl(int pid/*, File input, File output, File error*/) {
+        this.pid = pid;
+        //this.input = input;
+        //this.output = output;
+        //this.error = error;
+    }
+
+    public int getPid() {
+        return pid;
+    }
+
+    public boolean isRunning() throws IOException {
+        if (ScriptUtils.isWindows()) {
+            Map<String, String> props = new HashMap<String, String>();
+            props.put("${pid}", Integer.toString(pid));
+            int ret = ScriptUtils.execute("running", props);
+            return ret == 0;
+        } else {
+            try {
+                java.lang.Process process = new java.lang.ProcessBuilder("ps", "-p", Integer.toString(pid)).start();
+                BufferedReader r = new BufferedReader(new InputStreamReader(process.getInputStream()));
+                r.readLine(); // skip headers
+                String s = r.readLine();
+                boolean running = s != null && s.length() > 0;
+                int ret = process.waitFor();
+                return running;
+            } catch (InterruptedException e) {
+                throw new InterruptedIOException();
+            }
+        }
+    }
+
+    public void destroy() throws IOException {
+        int ret;
+        if (ScriptUtils.isWindows()) {
+            Map<String, String> props = new HashMap<String, String>();
+            props.put("${pid}", Integer.toString(pid));
+            ret = ScriptUtils.execute("destroy", props);
+        } else {
+            ret = ScriptUtils.executeProcess(new java.lang.ProcessBuilder("kill", "-9", Integer.toString(pid)));
+        }
+        if (ret != 0) {
+            throw new IOException("Unable to destroy proces, it may be already terminated");
+        }
+    }
+
+    /*
+    public OutputStream getInputStream() throws FileNotFoundException {
+        return new FileOutputStream(input);
+    }
+
+    public InputStream getOutputStream() throws FileNotFoundException {
+        return new FileInputStream(output);
+    }
+
+    public InputStream getErrorStream() throws FileNotFoundException {
+        return new FileInputStream(error);
+    }
+    */
+
+    public int waitFor() throws InterruptedException {
+        return 0;
+    }
+
+    public int exitValue() {
+        return 0;
+    }
+
+    public static Process create(File dir, String command) throws IOException {
+        //File input = File.createTempFile("jpm.", ".input");
+        //File output = File.createTempFile("jpm.", ".output");
+        //File error = File.createTempFile("jpm.", ".error");
+        File pidFile = File.createTempFile("jpm.", ".pid");
+        try {
+            Map<String, String> props = new HashMap<String, String>();
+            //props.put("${in.file}", input.getCanonicalPath());
+            //props.put("${out.file}", output.getCanonicalPath());
+            //props.put("${err.file}", error.getCanonicalPath());
+            props.put("${pid.file}", pidFile.getCanonicalPath());
+            props.put("${dir}", dir != null ? dir.getCanonicalPath() : "");
+            if (ScriptUtils.isWindows()) {
+                command = command.replaceAll("\"", "\"\"");
+            }
+            props.put("${command}", command);
+            int ret = ScriptUtils.execute("start", props);
+            if (ret != 0) {
+                throw new IOException("Unable to create process (error code: " + ret + ")");
+            }
+            int pid = readPid(pidFile);
+            return new ProcessImpl(pid/*, input, output, error*/);
+        } finally {
+            pidFile.delete();
+        }
+    }
+
+    public static Process attach(int pid) throws IOException {
+        return new ProcessImpl(pid);
+    }
+
+    private static int readPid(File pidFile) throws IOException {
+        InputStream is = new FileInputStream(pidFile);
+        try {
+            BufferedReader r = new BufferedReader(new InputStreamReader(is));
+            String pidString = r.readLine();
+            return Integer.valueOf(pidString);
+        } finally {
+            try {
+                is.close();
+            } catch (IOException e) {}
+        }
+    }
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ScriptUtils.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ScriptUtils.java
new file mode 100644
index 0000000..869c958
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ScriptUtils.java
@@ -0,0 +1,124 @@
+/*
+ * 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.servicemix.jpm.impl;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.Map;
+import java.util.Scanner;
+
+public class ScriptUtils {
+
+    public static int execute(String name, Map<String, String> props) throws IOException {
+        File script = File.createTempFile("jpm.", ".script");
+        try {
+            if (isWindows()) {
+                String res = "windows/" + name + ".vbs";
+                ScriptUtils.copyFilteredResource(res, script, props);
+                return executeProcess(new java.lang.ProcessBuilder("cscript",
+                                                                   "/NOLOGO",
+                                                                   "//E:vbs",
+                                                                   script.getCanonicalPath()));
+            } else {
+                String res = "unix/" + name + ".sh";
+                ScriptUtils.copyFilteredResource(res, script, props);
+                return executeProcess(new java.lang.ProcessBuilder("/bin/sh",
+                                                                   script.getCanonicalPath()));
+            }
+        } finally {
+            script.delete();
+        }
+    }
+
+    public static int executeProcess(java.lang.ProcessBuilder builder) throws IOException {
+        try {
+            java.lang.Process process = builder.start();
+            return process.waitFor();
+        } catch (InterruptedException e) {
+            throw new InterruptedIOException();
+        }
+    }
+
+    public static void copyFilteredResource(String resource, File outFile, Map<String, String> props) throws IOException {
+        InputStream is = null;
+        try {
+            is = ScriptUtils.class.getResourceAsStream(resource);
+            // Read it line at a time so that we can use the platform line ending when we write it out.
+            PrintStream out = new PrintStream(new FileOutputStream(outFile));
+            try {
+                Scanner scanner = new Scanner(is);
+                while (scanner.hasNextLine() ) {
+                    String line = scanner.nextLine();
+                    line = filter(line, props);
+                    out.println(line);
+                }
+            } finally {
+                safeClose(out);
+            }
+        } finally {
+            safeClose(is);
+        }
+    }
+
+    private static void safeClose(InputStream is) throws IOException {
+        if (is == null) {
+            return;
+        }
+        try {
+            is.close();
+        } catch (Throwable ignore) {
+        }
+    }
+
+    private static void safeClose(OutputStream is) throws IOException {
+        if (is == null) {
+            return;
+        }
+        try {
+            is.close();
+        } catch (Throwable ignore) {
+        }
+    }
+
+    private static String filter(String line, Map<String, String> props) {
+        for (Map.Entry<String, String> i : props.entrySet()) {
+            int p1 = line.indexOf(i.getKey());
+            if( p1 >= 0 ) {
+                String l1 = line.substring(0, p1);
+                String l2 = line.substring(p1+i.getKey().length());
+                line = l1+i.getValue()+l2;
+            }
+        }
+        return line;
+    }
+
+    private static final boolean windows;
+
+    static {
+        windows = System.getProperty("os.name").toLowerCase().indexOf("windows") != -1;
+    }
+
+    public static boolean isWindows() {
+        return windows;
+    }
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/AdminService.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/AdminService.java
new file mode 100644
index 0000000..cbf7181
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/AdminService.java
@@ -0,0 +1,27 @@
+/*
+ * 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.servicemix.kernel.gshell.admin;
+
+public interface AdminService {
+
+    Instance createInstance(String name, int port, String location) throws Exception;
+
+    Instance[] getInstances();
+
+    Instance getInstance(String name);
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/AdminServiceMBean.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/AdminServiceMBean.java
new file mode 100644
index 0000000..8a68d2d
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/AdminServiceMBean.java
@@ -0,0 +1,37 @@
+/*
+ * 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.servicemix.kernel.gshell.admin;
+
+public interface AdminServiceMBean {
+
+    void createInstance(String name, int port, String location) throws Exception;
+
+    String[] getInstances();
+
+    int getPort(String name) throws Exception;
+
+    void changePort(String name, int port) throws Exception;
+
+    String getState(String name) throws Exception;
+
+    void start(String name, String javaOpts) throws Exception;
+
+    void stop(String name) throws Exception;
+
+    void destroy(String name) throws Exception;
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/Instance.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/Instance.java
new file mode 100644
index 0000000..07612aa
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/Instance.java
@@ -0,0 +1,43 @@
+/*
+ * 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.servicemix.kernel.gshell.admin;
+
+public interface Instance {
+
+    String STOPPED = "Stopped";
+    String STARTING = "Starting";
+    String STARTED = "Started";
+
+    String getName();
+
+    String getLocation();
+
+    int getPid();
+
+    int getPort() throws Exception;
+
+    void changePort(int port) throws Exception;
+
+    void start(String javaOpts) throws Exception;
+
+    void stop() throws Exception;
+
+    void destroy() throws Exception;
+
+    String getState() throws Exception;
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/AdminServiceImpl.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/AdminServiceImpl.java
new file mode 100644
index 0000000..54283a8
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/AdminServiceImpl.java
@@ -0,0 +1,299 @@
+/*
+ * 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.servicemix.kernel.gshell.admin.internal;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.ServerSocket;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Scanner;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.geronimo.gshell.shell.ShellContext;
+import org.apache.geronimo.gshell.shell.ShellContextHolder;
+import org.apache.servicemix.kernel.gshell.admin.AdminService;
+import org.apache.servicemix.kernel.gshell.admin.Instance;
+import org.osgi.service.prefs.BackingStoreException;
+import org.osgi.service.prefs.Preferences;
+import org.osgi.service.prefs.PreferencesService;
+import org.springframework.beans.factory.InitializingBean;
+
+public class AdminServiceImpl implements AdminService, InitializingBean {
+
+    private static final Log LOGGER = LogFactory.getLog(AdminServiceImpl.class);
+
+    private PreferencesService preferences;
+
+    private Map<String, Instance> instances = new HashMap<String, Instance>();
+
+    private int defaultPortStart = 8101;
+
+    public PreferencesService getPreferences() {
+        return preferences;
+    }
+
+    public void setPreferences(PreferencesService preferences) {
+        this.preferences = preferences;
+    }
+
+    public synchronized void afterPropertiesSet() throws Exception {
+        try {
+            Preferences prefs = preferences.getUserPreferences("AdminServiceState");
+            Preferences child = prefs.node("Instances");
+            int count = child.getInt("count", 0);
+            Map<String, Instance> newInstances = new HashMap<String, Instance>();
+            for (int i = 0; i < count; i++) {
+                String name = child.get("item." + i + ".name", null);
+                String loc = child.get("item." + i + ".loc", null);
+                int pid = child.getInt("item." + i + ".pid", 0);
+                if (name != null) {
+                    InstanceImpl instance = new InstanceImpl(this, name, loc);
+                    if (pid > 0) {
+                        try {
+                            instance.attach(pid);
+                        } catch (IOException e) {
+                            // Ignore
+                        }
+                    }
+                    newInstances.put(name, instance);
+                }
+            }
+            instances = newInstances;
+        } catch (Exception e) {
+            LOGGER.warn("Unable to reload ServiceMix instance list", e);
+        }
+    }
+
+    public synchronized Instance createInstance(String name, int port, String location) throws Exception {
+        if (instances.get(name) != null) {
+            throw new IllegalArgumentException("Instance '" + name + "' already exists");
+        }
+        File serviceMixBase = new File(location != null ? location : ("instances/" + name)).getCanonicalFile();
+        int sshPort = port;
+        if (sshPort <= 0) {
+            try {
+                Preferences prefs = preferences.getUserPreferences("AdminServiceState");
+                sshPort = prefs.getInt("port", defaultPortStart + 1);
+                prefs.putInt("port", sshPort + 1);
+                prefs.flush();
+                prefs.sync();
+            } catch (Exception e) {
+                try {
+                    ServerSocket ss = new ServerSocket(0);
+                    sshPort = ss.getLocalPort();
+                    ss.close();
+                } catch (Exception t) {
+                }
+            }
+            if (sshPort <= 0) {
+                sshPort = defaultPortStart;
+            }
+        }
+        println("Creating new instance on port " + sshPort + " at: @|bold " + serviceMixBase + "|");
+
+        mkdir(serviceMixBase, "bin");
+        mkdir(serviceMixBase, "etc");
+        mkdir(serviceMixBase, "system");
+        mkdir(serviceMixBase, "deploy");
+        mkdir(serviceMixBase, "data");
+
+        copyResourceToDir(serviceMixBase, "etc/config.properties", true);
+        copyResourceToDir(serviceMixBase, "etc/org.apache.servicemix.features.cfg", true);
+        copyResourceToDir(serviceMixBase, "etc/users.properties", true);
+        copyResourceToDir(serviceMixBase, "etc/org.ops4j.pax.logging.cfg", true);
+        copyResourceToDir(serviceMixBase, "etc/org.ops4j.pax.url.mvn.cfg", true);
+        copyResourceToDir(serviceMixBase, "etc/startup.properties", true);
+
+        HashMap<String, String> props = new HashMap<String, String>();
+        props.put("${servicemix.name}", name);
+        props.put("${servicemix.home}", System.getProperty("servicemix.home"));
+        props.put("${servicemix.base}", serviceMixBase.getPath());
+        props.put("${servicemix.sshPort}", Integer.toString(sshPort));
+        copyFilteredResourceToDir(serviceMixBase, "etc/system.properties", props);
+        copyFilteredResourceToDir(serviceMixBase, "etc/org.apache.servicemix.shell.cfg", props);
+        if( System.getProperty("os.name").startsWith("Win") ) {
+            copyFilteredResourceToDir(serviceMixBase, "bin/servicemix.bat", props);
+        } else {
+            copyFilteredResourceToDir(serviceMixBase, "bin/servicemix", props);
+            chmod(new File(serviceMixBase, "bin/servicemix"), "a+x");
+        }
+        Instance instance = new InstanceImpl(this, name, serviceMixBase.toString());
+        instances.put(name, instance);
+        saveState();
+        return instance;
+    }
+
+    public synchronized Instance[] getInstances() {
+        return instances.values().toArray(new Instance[0]);
+    }
+
+    public synchronized Instance getInstance(String name) {
+        return instances.get(name);
+    }
+
+    synchronized void forget(String name) {
+        instances.remove(name);
+    }
+
+    synchronized void saveState() throws IOException, BackingStoreException {
+        Preferences prefs = preferences.getUserPreferences("AdminServiceState");
+        Preferences child = prefs.node("Instances");
+        child.clear();
+        Instance[] data = getInstances();
+        child.putInt("count", data.length);
+        for (int i = 0; i < data.length; i++) {
+            child.put("item." + i + ".name", data[i].getName());
+            child.put("item." + i + ".loc", data[i].getLocation());
+            child.putInt("item." + i + ".pid", data[i].getPid());
+        }
+        prefs.flush();
+        prefs.sync();
+    }
+
+    private void copyResourceToDir(File target, String resource, boolean text) throws Exception {
+        File outFile = new File(target, resource);
+        if( !outFile.exists() ) {
+            println("Creating file: @|bold " + outFile.getPath() + "|");
+            InputStream is = getClass().getClassLoader().getResourceAsStream("/org/apache/servicemix/kernel/gshell/admin/" + resource);
+            try {
+                if( text ) {
+                    // Read it line at a time so that we can use the platform line ending when we write it out.
+                    PrintStream out = new PrintStream(new FileOutputStream(outFile));
+                    try {
+                        Scanner scanner = new Scanner(is);
+                        while (scanner.hasNextLine() ) {
+                            String line = scanner.nextLine();
+                            out.println(line);
+                        }
+                    } finally {
+                        safeClose(out);
+                    }
+                } else {
+                    // Binary so just write it out the way it came in.
+                    FileOutputStream out = new FileOutputStream(new File(target, resource));
+                    try {
+                        int c=0;
+                        while((c=is.read())>=0) {
+                            out.write(c);
+                        }
+                    } finally {
+                        safeClose(out);
+                    }
+                }
+            } finally {
+                safeClose(is);
+            }
+        }
+    }
+
+    private void println(String st) {
+        ShellContext ctx = ShellContextHolder.get(true);
+        if (ctx != null) {
+            ctx.getIo().out.println(st);
+        } else {
+            System.out.println(st);
+        }
+    }
+
+    private void copyFilteredResourceToDir(File target, String resource, HashMap<String, String> props) throws Exception {
+        File outFile = new File(target, resource);
+        if( !outFile.exists() ) {
+            println("Creating file: @|bold "+outFile.getPath()+"|");
+            InputStream is = getClass().getClassLoader().getResourceAsStream("/org/apache/servicemix/kernel/gshell/admin/" + resource);
+            try {
+                // Read it line at a time so that we can use the platform line ending when we write it out.
+                PrintStream out = new PrintStream(new FileOutputStream(outFile));
+                try {
+                    Scanner scanner = new Scanner(is);
+                    while (scanner.hasNextLine() ) {
+                        String line = scanner.nextLine();
+                        line = filter(line, props);
+                        out.println(line);
+                    }
+                } finally {
+                    safeClose(out);
+                }
+            } finally {
+                safeClose(is);
+            }
+        }
+    }
+
+    private void safeClose(InputStream is) throws IOException {
+        if (is == null) {
+            return;
+        }
+        try {
+            is.close();
+        } catch (Throwable ignore) {
+        }
+    }
+
+    private void safeClose(OutputStream is) throws IOException {
+        if (is == null) {
+            return;
+        }
+        try {
+            is.close();
+        } catch (Throwable ignore) {
+        }
+    }
+
+    private String filter(String line, HashMap<String, String> props) {
+        for (Map.Entry<String, String> i : props.entrySet()) {
+            int p1 = line.indexOf(i.getKey());
+            if( p1 >= 0 ) {
+                String l1 = line.substring(0, p1);
+                String l2 = line.substring(p1+i.getKey().length());
+                line = l1+i.getValue()+l2;
+            }
+        }
+        return line;
+    }
+
+    private void mkdir(File serviceMixBase, String path) {
+        File file = new File(serviceMixBase, path);
+        if( !file.exists() ) {
+            println("Creating dir:  @|bold "+file.getPath()+"|");
+            file.mkdirs();
+        }
+    }
+
+    private int chmod(File serviceFile, String mode) throws Exception {
+        ProcessBuilder builder = new ProcessBuilder();
+        builder.command("chmod", mode, serviceFile.getCanonicalPath());
+        Process p = builder.start();
+
+        // gnodet: Fix SMX4KNL-46: cpu goes to 100% after running the 'admin create' command
+        // Not sure exactly what happens, but commenting the process io redirection seems
+        // to work around the problem.
+        //
+        //PumpStreamHandler handler = new PumpStreamHandler(io.inputStream, io.outputStream, io.errorStream);
+        //handler.attach(p);
+        //handler.start();
+        int status = p.waitFor();
+        //handler.stop();
+        return status;
+    }
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/AdminServiceMBeanImpl.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/AdminServiceMBeanImpl.java
new file mode 100644
index 0000000..a53b663
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/AdminServiceMBeanImpl.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.admin.internal;
+
+import org.apache.servicemix.kernel.gshell.admin.AdminService;
+import org.apache.servicemix.kernel.gshell.admin.AdminServiceMBean;
+import org.apache.servicemix.kernel.gshell.admin.Instance;
+
+public class AdminServiceMBeanImpl implements AdminServiceMBean {
+
+    private AdminService adminService;
+
+    public AdminService getAdminService() {
+        return adminService;
+    }
+
+    public void setAdminService(AdminService adminService) {
+        this.adminService = adminService;
+    }
+
+    public void createInstance(String name, int port, String location) throws Exception {
+        adminService.createInstance(name, port, location);
+    }
+
+    public String[] getInstances() {
+        Instance[] instances = adminService.getInstances();
+        String[] names = new String[instances.length];
+        for (int i = 0; i < instances.length; i++) {
+            names[i] = instances[i].getName();
+        }
+        return names;
+    }
+
+    public int getPort(String name) throws Exception {
+        return getExistingInstance(name).getPort();
+    }
+
+    public void changePort(String name, int port) throws Exception {
+        getExistingInstance(name).changePort(port);
+    }
+
+    public String getState(String name) throws Exception {
+        return getExistingInstance(name).getState();
+    }
+
+    public void start(String name, String javaOpts) throws Exception {
+        getExistingInstance(name).start(javaOpts);
+    }
+
+    public void stop(String name) throws Exception {
+        getExistingInstance(name).stop();
+    }
+
+    public void destroy(String name) throws Exception {
+        getExistingInstance(name).destroy();
+    }
+
+
+    private Instance getExistingInstance(String name) {
+        Instance i = adminService.getInstance(name);
+        if (i == null) {
+            throw new IllegalArgumentException("Instance '" + name + "' does not exist");
+        }
+        return i;
+    }
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/InstanceImpl.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/InstanceImpl.java
new file mode 100644
index 0000000..3815ca5
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/InstanceImpl.java
@@ -0,0 +1,220 @@
+/*
+ * 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.servicemix.kernel.gshell.admin.internal;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.servicemix.jpm.Process;
+import org.apache.servicemix.jpm.ProcessBuilderFactory;
+import org.apache.servicemix.jpm.impl.ScriptUtils;
+import org.apache.servicemix.kernel.gshell.admin.Instance;
+
+public class InstanceImpl implements Instance {
+
+    private static final Log LOG = LogFactory.getLog(InstanceImpl.class);
+
+    private AdminServiceImpl service;
+    private String name;
+    private String location;
+    private Process process;
+
+    public InstanceImpl(AdminServiceImpl service, String name, String location) {
+        this.service = service;
+        this.name = name;
+        this.location = location;
+    }
+
+    public void attach(int pid) throws IOException {
+        checkProcess();
+        if (this.process != null) {
+            throw new IllegalStateException("Instance already started");
+        }
+        this.process = ProcessBuilderFactory.newInstance().newBuilder().attach(pid);
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public String getLocation() {
+        return location;
+    }
+
+    public int getPid() {
+        checkProcess();
+        return this.process != null ? this.process.getPid() : 0;
+    }
+
+    public int getPort() throws Exception {
+        InputStream is = null;
+        try {
+            File f = new File(location, "etc/org.apache.servicemix.shell.cfg");
+            is = new FileInputStream(f);
+            Properties props = new Properties();
+            props.load(is);
+            String loc = props.getProperty("sshPort");
+            return Integer.parseInt(loc);
+        } finally {
+            if (is != null) {
+                is.close();
+            }
+        }
+    }
+
+    public void changePort(int port) throws Exception {
+        checkProcess();
+        if (this.process != null) {
+            throw new IllegalStateException("Instance not stopped");
+        }
+        Properties props = new Properties();
+        File f = new File(location, "etc/org.apache.servicemix.shell.cfg");
+        InputStream is = new FileInputStream(f);
+        try {
+            props.load(is);
+        } finally {
+            is.close();
+        }
+        props.setProperty("sshPort", Integer.toString(port));
+        OutputStream os = new FileOutputStream(f);
+        try {
+            props.store(os, null);
+        } finally {
+            os.close();
+        }
+    }
+
+    public synchronized void start(String javaOpts) throws Exception {
+        checkProcess();
+        if (this.process != null) {
+            throw new IllegalStateException("Instance already started");
+        }
+        if (javaOpts == null) {
+            javaOpts = "-server -Xmx512M -Dcom.sun.management.jmxremote";
+        }
+        File libDir = new File(System.getProperty("servicemix.home"), "lib");
+        File[] jars = libDir.listFiles(new FilenameFilter() {
+            public boolean accept(File dir, String name) {
+                return name.endsWith(".jar");
+            }
+        });
+        StringBuilder classpath = new StringBuilder();
+        for (File jar : jars) {
+            if (classpath.length() > 0) {
+                classpath.append(System.getProperty("path.separator"));
+            }
+            classpath.append(jar.getCanonicalPath());
+        }
+        String command = new File(System.getProperty("java.home"), ScriptUtils.isWindows() ? "bin\\java.exe" : "bin/java").getCanonicalPath()
+                + " " + javaOpts
+                + " -Dservicemix.home=\"" + System.getProperty("servicemix.home") + "\""
+                + " -Dservicemix.base=\"" + new File(location).getCanonicalPath() + "\""
+                + " -Dservicemix.startLocalConsole=false"
+                + " -Dservicemix.startRemoteShell=true"
+                + " -classpath " + classpath.toString()
+                + " org.apache.servicemix.kernel.main.Main";
+        LOG.debug("Starting instance with command: " + command);
+        this.process = ProcessBuilderFactory.newInstance().newBuilder()
+                        .directory(new File(location))
+                        .command(command)
+                        .start();
+        this.service.saveState();
+    }
+
+    public synchronized void stop() throws Exception {
+        checkProcess();
+        if (this.process == null) {
+            throw new IllegalStateException("Instance not started");
+        }
+        this.process.destroy();
+    }
+
+    public synchronized void destroy() throws Exception {
+        checkProcess();
+        if (this.process != null) {
+            throw new IllegalStateException("Instance not stopped");
+        }
+        deleteFile(new File(location));
+        this.service.forget(name);
+        this.service.saveState();
+    }
+
+
+    public synchronized String getState() {
+        checkProcess();
+        if (this.process == null) {
+            return STOPPED;
+        } else {
+            try {
+                int port = getPort();
+                Socket s = new Socket("localhost", port);
+                s.close();
+                return STARTED;
+            } catch (Exception e) {
+                // ignore
+            }
+            return STARTING;
+        }
+    }
+
+    protected void checkProcess() {
+        if (this.process != null) {
+            try {
+                if (!this.process.isRunning()) {
+                    this.process = null;
+                }
+            } catch (IOException e) {
+            }
+        }
+    }
+
+    protected static boolean deleteFile(File fileToDelete) {
+        if (fileToDelete == null || !fileToDelete.exists()) {
+            return true;
+        }
+        boolean result = true;
+        if (fileToDelete.isDirectory()) {
+            File[] files = fileToDelete.listFiles();
+            if (files == null) {
+                result = false;
+            } else {
+                for (int i = 0; i < files.length; i++) {
+                    File file = files[i];
+                    if (file.getName().equals(".") || file.getName().equals("..")) {
+                        continue;
+                    }
+                    if (file.isDirectory()) {
+                        result &= deleteFile(file);
+                    } else {
+                        result &= file.delete();
+                    }
+                }
+            }
+        }
+        result &= fileToDelete.delete();
+        return result;
+    }
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/AdminCommandSupport.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/AdminCommandSupport.java
new file mode 100644
index 0000000..9da97b4
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/AdminCommandSupport.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.servicemix.kernel.gshell.admin.internal.commands;
+
+import org.apache.servicemix.kernel.gshell.admin.AdminService;
+import org.apache.servicemix.kernel.gshell.admin.Instance;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+
+public abstract class AdminCommandSupport extends OsgiCommandSupport {
+
+    private AdminService adminService;
+
+    public AdminService getAdminService() {
+        return adminService;
+    }
+
+    public void setAdminService(AdminService adminService) {
+        this.adminService = adminService;
+    }
+
+    protected Instance getExistingInstance(String name) {
+        Instance i = adminService.getInstance(name);
+        if (i == null) {
+            throw new IllegalArgumentException("Instance '" + name + "' does not exist");
+        }
+        return i;
+    }
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ChangePortCommand.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ChangePortCommand.java
new file mode 100644
index 0000000..adbf2f6
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ChangePortCommand.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.admin.internal.commands;
+
+import org.apache.geronimo.gshell.clp.Argument;
+
+public class ChangePortCommand extends AdminCommandSupport {
+
+    @Argument(index=0, required=true, description="The instance name")
+    private String instance = null;
+
+    @Argument(index=1, required=true, description="The new port")
+    private int port = 0;
+
+    protected Object doExecute() throws Exception {
+        getExistingInstance(instance).changePort(port);
+        return Result.SUCCESS;
+    }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ConnectCommand.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ConnectCommand.java
new file mode 100644
index 0000000..244b9f8
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ConnectCommand.java
@@ -0,0 +1,41 @@
+/*
+ * 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.servicemix.kernel.gshell.admin.internal.commands;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.geronimo.gshell.shell.ShellContextHolder;
+
+public class ConnectCommand extends AdminCommandSupport {
+
+    @Argument(index=0, required=true, description="The instance name")
+    private String instance = null;
+
+    @Option(name="-u", aliases={"--username"}, token="USERNAME", description="Remote user name")
+    private String username = "smx";
+
+    @Option(name="-p", aliases={"--password"}, token="PASSWORD", description="Remote user password")
+    private String password = "smx";
+
+    protected Object doExecute() throws Exception {
+        int port = getExistingInstance(instance).getPort();
+        ShellContextHolder.get().getShell().execute("ssh -l " + username + " -P " + password + " -p " + port + " localhost");
+        return Result.SUCCESS;
+    }
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/CreateCommand.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/CreateCommand.java
new file mode 100644
index 0000000..d13c381
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/CreateCommand.java
@@ -0,0 +1,43 @@
+/*
+ * 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.servicemix.kernel.gshell.admin.internal.commands;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+
+/**
+ * Creates a new servicemix instance 
+ *
+ * @version $Rev: 679826 $ $Date: 2008-07-25 17:00:12 +0200 (Fri, 25 Jul 2008) $
+ */
+public class CreateCommand extends AdminCommandSupport
+{
+    @Option(name = "-p", aliases = { "--port"}, description = "Port number for remote shell connection")
+    private int port = 0;
+
+    @Option(name = "-l", aliases = { "--location"}, description = "Location of the new instance on the file system")
+    private String location;
+
+    @Argument(index=0, required=true, description="Name of the new ServiceMix instance")
+    private String instance = null;
+
+    protected Object doExecute() throws Exception {
+        getAdminService().createInstance(instance, port, location);
+        return Result.SUCCESS;
+    }
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/DestroyCommand.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/DestroyCommand.java
new file mode 100644
index 0000000..6084caa
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/DestroyCommand.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.admin.internal.commands;
+
+import org.apache.geronimo.gshell.clp.Argument;
+
+/**
+ * Creates a new servicemix instance
+ *
+ * @version $Rev: 679826 $ $Date: 2008-07-25 17:00:12 +0200 (Fri, 25 Jul 2008) $
+ */
+public class DestroyCommand extends AdminCommandSupport
+{
+    @Argument(index=0, required=true, description="The name of the ServiceMix instance to destroy")
+    private String instance = null;
+
+    protected Object doExecute() throws Exception {
+        getExistingInstance(instance).destroy();
+        return Result.SUCCESS;
+    }
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ListCommand.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ListCommand.java
new file mode 100644
index 0000000..61f3afb
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ListCommand.java
@@ -0,0 +1,68 @@
+/*
+ * 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.servicemix.kernel.gshell.admin.internal.commands;
+
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.servicemix.kernel.gshell.admin.Instance;
+
+/**
+ * List available instances
+ */
+public class ListCommand extends AdminCommandSupport {
+
+    @Option(name = "-l", aliases = { "--location" }, description = "Display instances location")
+    boolean location;
+
+    protected Object doExecute() throws Exception {
+        Instance[] instances = getAdminService().getInstances();
+        if (location) {
+            io.out.println("  Port   State       Pid  Location");
+        } else {
+            io.out.println("  Port   State       Pid  Name");
+        }
+        for (Instance instance : instances) {
+            StringBuilder sb = new StringBuilder();
+            sb.append('[');
+            String s = Integer.toString(instance.getPort());
+            for (int i = s.length(); i < 5; i++) {
+                sb.append(' ');
+            }
+            sb.append(s);
+            sb.append("] [");
+            String state = instance.getState();
+            while (state.length() < "starting".length()) {
+                state += " ";
+            }
+            sb.append(state);
+            sb.append("] [");
+            s = Integer.toString(instance.getPid());
+            for (int i = s.length(); i < 5; i++) {
+                sb.append(' ');
+            }
+            sb.append(s);
+            sb.append("] ");
+            if (location) {
+                sb.append(instance.getLocation());
+            } else {
+                sb.append(instance.getName());
+            }
+            io.out.println(sb.toString());
+        }
+        return Result.SUCCESS;
+    }
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/StartCommand.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/StartCommand.java
new file mode 100644
index 0000000..77afa91
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/StartCommand.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.admin.internal.commands;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+
+public class StartCommand extends AdminCommandSupport {
+
+    @Option(name = "-o", aliases = { "--java-opts"}, description = "Java options when launching the instance")
+    private String javaOpts;
+
+    @Argument(index=0, required=true, description="The instance name")
+    private String instance = null;
+
+    protected Object doExecute() throws Exception {
+        getExistingInstance(instance).start(javaOpts);
+        return Result.SUCCESS;
+    }
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/StopCommand.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/StopCommand.java
new file mode 100644
index 0000000..c449387
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/StopCommand.java
@@ -0,0 +1,30 @@
+/*
+ * 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.servicemix.kernel.gshell.admin.internal.commands;
+
+import org.apache.geronimo.gshell.clp.Argument;
+
+public class StopCommand extends AdminCommandSupport {
+
+    @Argument(index=0, required=true, description="The instance name")
+    private String instance = null;
+
+    protected Object doExecute() throws Exception {
+        getExistingInstance(instance).stop();
+        return Result.SUCCESS;
+    }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/completers/InstanceCompleter.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/completers/InstanceCompleter.java
new file mode 100644
index 0000000..cfcb95a
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/completers/InstanceCompleter.java
@@ -0,0 +1,46 @@
+/*
+ * 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.servicemix.kernel.gshell.admin.internal.completers;
+
+import java.util.List;
+
+import org.apache.servicemix.kernel.gshell.admin.AdminService;
+import org.apache.servicemix.kernel.gshell.admin.Instance;
+import org.apache.geronimo.gshell.console.completer.StringsCompleter;
+import jline.Completor;
+
+/**
+ * {@link jline.Completor} for server instance names.
+ *
+ * Displays a list of configured server instances for the Admin commands.
+ *
+ */
+public class InstanceCompleter implements Completor {
+    private AdminService adminService;
+
+    public void setAdminService(AdminService adminService) {
+        this.adminService = adminService;
+    }
+
+    public int complete(String buffer, int cursor, List candidates) {
+        StringsCompleter delegate = new StringsCompleter();
+        for (Instance instance : adminService.getInstances()) {
+            delegate.getStrings().add(instance.getName());
+        }
+        return delegate.complete(buffer, cursor, candidates);
+    }
+}
diff --git a/karaf/gshell/gshell-admin/src/main/resources/META-INF/spring/gshell-admin.xml b/karaf/gshell/gshell-admin/src/main/resources/META-INF/spring/gshell-admin.xml
new file mode 100644
index 0000000..5f05513
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/META-INF/spring/gshell-admin.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:osgi="http://www.springframework.org/schema/osgi"
+       xmlns:osgix="http://www.springframework.org/schema/osgi-compendium"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+       xsi:schemaLocation="
+  http://www.springframework.org/schema/beans
+  http://www.springframework.org/schema/beans/spring-beans.xsd
+  http://www.springframework.org/schema/osgi
+  http://www.springframework.org/schema/osgi/spring-osgi.xsd
+  http://www.springframework.org/schema/osgi-compendium
+  http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd
+  http://www.springframework.org/schema/util
+  http://www.springframework.org/schema/util/spring-util.xsd
+  http://servicemix.apache.org/schema/servicemix-gshell
+  http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+    <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+    <bean id="adminCommandBundleSupport" scope="prototype">
+        <property name="adminService" ref="adminService" />
+    </bean>
+
+    <gshell:command-bundle>
+        <gshell:command name="admin/create">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.admin.internal.commands.CreateCommand"
+                           parent="adminCommandBundleSupport" />
+        </gshell:command>
+        <gshell:command name="admin/connect">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.admin.internal.commands.ConnectCommand"
+                           parent="adminCommandBundleSupport" />
+            <gshell:completers>
+                <ref bean="instanceCompleter" />
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+        <gshell:command name="admin/list">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.admin.internal.commands.ListCommand"
+                           parent="adminCommandBundleSupport" />
+        </gshell:command>
+        <gshell:command name="admin/start">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.admin.internal.commands.StartCommand"
+                           parent="adminCommandBundleSupport" />
+            <gshell:completers>
+                <ref bean="instanceCompleter" />
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+        <gshell:command name="admin/stop">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.admin.internal.commands.StopCommand"
+                           parent="adminCommandBundleSupport" />
+            <gshell:completers>
+                <ref bean="instanceCompleter" />
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+        <gshell:command name="admin/destroy">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.admin.internal.commands.DestroyCommand"
+                           parent="adminCommandBundleSupport" />
+            <gshell:completers>
+                <ref bean="instanceCompleter" />
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+        <gshell:command name="admin/change-port">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.admin.internal.commands.ChangePortCommand"
+                           parent="adminCommandBundleSupport" />
+            <gshell:completers>
+                <ref bean="instanceCompleter" />
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+    </gshell:command-bundle>
+
+    <osgi:reference id="preferences" interface="org.osgi.service.prefs.PreferencesService" cardinality="0..1" />
+
+    <bean id="adminService" class="org.apache.servicemix.kernel.gshell.admin.internal.AdminServiceImpl">
+        <property name="preferences" ref="preferences" />
+    </bean>
+
+    <bean id="instanceCompleter" class="org.apache.servicemix.kernel.gshell.admin.internal.completers.InstanceCompleter">
+        <property name="adminService" ref="adminService" />
+    </bean>
+
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/unix/start.sh b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/unix/start.sh
new file mode 100644
index 0000000..1d1d720
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/unix/start.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+################################################################################
+#
+#    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.
+#
+################################################################################
+
+#exec 1>${out.file}
+#exec 2>${err.file}
+exec 1>/dev/null
+exec 2>/dev/null
+if [ "x${dir}" != "x" ]; then
+    cd ${dir}
+fi
+nohup ${command} &
+echo $! > ${pid.file}
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/destroy.vbs b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/destroy.vbs
new file mode 100644
index 0000000..abd60eb
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/destroy.vbs
@@ -0,0 +1,27 @@
+'===============================================================================
+'
+'    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.
+'
+'===============================================================================
+
+Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
+Set colProcessList = objWMIService.ExecQuery("Select * from Win32_Process Where ProcessId = ${pid}")
+intRetVal = 1
+For Each objProcess in colProcessList
+    objProcess.Terminate()
+    intRetVal = 0
+Next
+WScript.Quit(intRetVal)
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/running.vbs b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/running.vbs
new file mode 100644
index 0000000..32c65c5
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/running.vbs
@@ -0,0 +1,26 @@
+'===============================================================================
+'
+'    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.
+'
+'===============================================================================
+
+Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
+Set colProcessList = objWMIService.ExecQuery("Select * from Win32_Process Where ProcessId = ${pid}")
+intRetVal = 1
+For Each objProcess in colProcessList
+    intRetVal = 0
+Next
+WScript.Quit(intRetVal)
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/start.vbs b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/start.vbs
new file mode 100644
index 0000000..6004c86
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/start.vbs
@@ -0,0 +1,34 @@
+'===============================================================================
+'
+'    Licensed to the Apache Software Foundation (ASF) under one or more
+'    contributor license agreements.  See the NOTICE file distributed with
+'    this work for additional information regarding copyright ownership.
+'    The ASF licenses this file to You under the Apache License, Version 2.0
+'    (the "License"); you may not use this file except in compliance with
+'    the License.  You may obtain a copy of the License at
+'
+'       http://www.apache.org/licenses/LICENSE-2.0
+'
+'    Unless required by applicable law or agreed to in writing, software
+'    distributed under the License is distributed on an "AS IS" BASIS,
+'    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+'    See the License for the specific language governing permissions and
+'    limitations under the License.
+'
+'===============================================================================
+
+Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
+Set objConfig = objWMIService.Get("Win32_ProcessStartup").SpawnInstance_
+objConfig.ShowWindow = SW_HIDE
+objConfig.CreateFlags = 8
+If Len("${dir}") > 0 Then
+    intReturn = objWMIService.Get("Win32_Process").Create("${command}", "${dir}", objConfig, intProcessID)
+Else
+    intReturn = objWMIService.Get("Win32_Process").Create("${command}", Null, objConfig, intProcessID)
+End If
+If intReturn = 0 Then
+    Set objOutputFile = CreateObject("Scripting.fileSystemObject").CreateTextFile("${pid.file}", TRUE)
+    objOutputFile.WriteLine(intProcessID)
+    objOutputFile.Close
+End If
+WScript.Quit(intReturn)
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/bin/servicemix b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/bin/servicemix
new file mode 100644
index 0000000..20a33e6
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/bin/servicemix
@@ -0,0 +1,25 @@
+#!/bin/sh
+################################################################################
+#
+#    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.
+#
+################################################################################
+
+SERVICEMIX_HOME=${servicemix.home}
+SERVICEMIX_BASE=${servicemix.base}
+
+export SERVICEMIX_BASE
+${SERVICEMIX_HOME}/bin/servicemix "$*"
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/bin/servicemix.bat b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/bin/servicemix.bat
new file mode 100644
index 0000000..13c155a
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/bin/servicemix.bat
@@ -0,0 +1,25 @@
+@ECHO OFF
+REM =========================================================================
+REM 
+REM Licensed to the Apache Software Foundation (ASF) under one or more
+REM contributor license agreements.  See the NOTICE file distributed with
+REM this work for additional information regarding copyright ownership.
+REM The ASF licenses this file to You under the Apache License, Version 2.0
+REM (the "License"); you may not use this file except in compliance with
+REM the License.  You may obtain a copy of the License at
+REM 
+REM    http://www.apache.org/licenses/LICENSE-2.0
+REM 
+REM Unless required by applicable law or agreed to in writing, software
+REM distributed under the License is distributed on an "AS IS" BASIS,
+REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+REM See the License for the specific language governing permissions and
+REM limitations under the License.
+REM 
+REM =========================================================================
+
+SET SERVICEMIX_BASE=${servicemix.base}
+SETLOCAL
+SET SERVICEMIX_HOME=${servicemix.home}
+
+%SERVICEMIX_HOME%\bin\servicemix.bat %*
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.apache.servicemix.shell.cfg b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.apache.servicemix.shell.cfg
new file mode 100644
index 0000000..e6a8fd2
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.apache.servicemix.shell.cfg
@@ -0,0 +1,23 @@
+################################################################################
+#
+#    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.
+#
+################################################################################
+
+#
+startLocalConsole=${servicemix.startLocalConsole}
+startRemoteShell=${servicemix.startRemoteShell}
+sshPort=${servicemix.sshPort}
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.ops4j.pax.logging.cfg b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.ops4j.pax.logging.cfg
new file mode 100644
index 0000000..24ddea5
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.ops4j.pax.logging.cfg
@@ -0,0 +1,36 @@
+################################################################################
+#
+#    Licensed to the Apache Software Foundation (ASF) under one or more
+#    contributor license agreements.  See the NOTICE file distributed with
+#    this work for additional information regarding copyright ownership.
+#    The ASF licenses this file to You under the Apache License, Version 2.0
+#    (the "License"); you may not use this file except in compliance with
+#    the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+#
+################################################################################
+
+# Root logger
+log4j.rootLogger=INFO, out, osgi:VmLogAppender
+
+# Logger infos
+log4j.logger.org.apache.geronimo.gshell.remote=WARN
+
+# CONSOLE appender not used by default
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+
+# File appender
+log4j.appender.out=org.apache.log4j.FileAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+log4j.appender.out.file=${servicemix.base}/data/log/servicemix.log
+log4j.appender.out.append=true
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.ops4j.pax.url.mvn.cfg b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.ops4j.pax.url.mvn.cfg
new file mode 100644
index 0000000..d0317cd
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.ops4j.pax.url.mvn.cfg
@@ -0,0 +1,73 @@
+################################################################################
+#
+#    Licensed to the Apache Software Foundation (ASF) under one or more
+#    contributor license agreements.  See the NOTICE file distributed with
+#    this work for additional information regarding copyright ownership.
+#    The ASF licenses this file to You under the Apache License, Version 2.0
+#    (the "License"); you may not use this file except in compliance with
+#    the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+#
+################################################################################
+
+#
+# If set to true, the following property will not allow any certificate to be used
+# when accessing maven repositories through SSL
+#
+#org.ops4j.pax.url.mvn.certificateCheck=
+
+#
+# Path to the local maven settings file.
+# The repositories defined in this file will be automatically added to the list
+# of default repositories if the 'org.ops4j.pax.url.mvn.repositories' property
+# below is not set.
+# The following locations are checked for the existence of the settings.xml file
+#   * 1. looks for the specified url
+#   * 2. if not found looks for ${user.home}/.m2/settings.xml
+#   * 3. if not found looks for ${maven.home}/conf/settings.xml
+#   * 4. if not found looks for ${M2_HOME}/conf/settings.xml
+#
+#org.ops4j.pax.url.mvn.settings=
+
+#
+# Path to the local maven repository which is used to avoid downloading
+# artifacts when they already exist locally.
+# The value of this property will be extracted from the settings.xml file
+# above, or defaulted to:
+#     System.getProperty( "user.home" ) + "/.m2/repository"
+#
+#org.ops4j.pax.url.mvn.localRepository=
+
+#
+# Comma separated list of repositories scanned when resolving an artifact.
+# Those repositories will be checked before iterating through the
+     below list of repositories and even before the local repository
+# A repository url can be appended with zero or more of the following flags:
+#    @snapshots  : the repository contains snaphots
+#    @noreleases : the repository does not contain any released artifacts
+#
+# The following property value will add the system folder as a repo.
+#
+org.ops4j.pax.url.mvn.defaultRepositories=file:${servicemix.home}/system@snapshots,file:${servicemix.base}/system@snapshots
+
+#
+# Comma separated list of repositories scanned when resolving an artifact.
+# The default list includes the following repositories:
+#    http://repo1.maven.org/maven2
+#    http://repository.ops4j.org/maven2
+# To add repositories to the default ones, prepend '+' to the list of repositories
+# to add.
+# A repository url can be appended with zero or more of the following flags:
+#    @snapshots  : the repository contains snaphots
+#    @noreleases : the repository does not contain any released artifacts
+#
+# The following property value will add the system folders as a repo.
+#
+org.ops4j.pax.url.mvn.repositories=file:${servicemix.base}/system@snapshots,file:${user.home}/.m2/repository@snapshots,http://repo1.maven.org/maven2,http://people.apache.org/repo/m2-snapshot-repository@snapshots@noreleases,http://svn.apache.org/repos/asf/servicemix/m2-repo
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/system.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/system.properties
new file mode 100644
index 0000000..255295e
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/system.properties
@@ -0,0 +1,21 @@
+################################################################################
+#
+#    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.
+#
+################################################################################
+
+org.ops4j.pax.logging.DefaultServiceLog.level=ERROR
+servicemix.name=${servicemix.name}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/users.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/users.properties
new file mode 100644
index 0000000..0348fae
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/users.properties
@@ -0,0 +1,21 @@
+################################################################################
+#
+#    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.
+#
+################################################################################
+
+#
+smx=smx,admin
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ChangePortCommand.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ChangePortCommand.properties
new file mode 100644
index 0000000..6a7edbf
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ChangePortCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Change the port of an existing instance.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ConnectCommand.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ConnectCommand.properties
new file mode 100644
index 0000000..1c78a34
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ConnectCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Connect to an existing instance.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/CreateCommand.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/CreateCommand.properties
new file mode 100644
index 0000000..532ada2
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/CreateCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Create a new instance.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/DestroyCommand.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/DestroyCommand.properties
new file mode 100644
index 0000000..cb0581c
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/DestroyCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Destroy an existing instance.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ListCommand.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ListCommand.properties
new file mode 100644
index 0000000..c473471
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ListCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=List existing instances.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/StartCommand.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/StartCommand.properties
new file mode 100644
index 0000000..dfb4046
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/StartCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Start an existing instance.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/StopCommand.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/StopCommand.properties
new file mode 100644
index 0000000..0cd3acd
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/StopCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Stop an existing instance.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/test/java/org/apache/servicemix/jpm/MainTest.java b/karaf/gshell/gshell-admin/src/test/java/org/apache/servicemix/jpm/MainTest.java
new file mode 100644
index 0000000..1833584
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/test/java/org/apache/servicemix/jpm/MainTest.java
@@ -0,0 +1,24 @@
+/*
+ * 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.servicemix.jpm;
+
+public class MainTest {
+
+    public static void main(String[] args) throws Exception {
+        Thread.sleep(Long.parseLong(args[0]));
+    }
+}
diff --git a/karaf/gshell/gshell-admin/src/test/java/org/apache/servicemix/jpm/ProcessTest.java b/karaf/gshell/gshell-admin/src/test/java/org/apache/servicemix/jpm/ProcessTest.java
new file mode 100644
index 0000000..8328813
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/test/java/org/apache/servicemix/jpm/ProcessTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.servicemix.jpm;
+
+import java.io.File;
+
+import junit.framework.TestCase;
+import org.apache.servicemix.jpm.impl.ScriptUtils;
+
+public class ProcessTest extends TestCase {
+
+    public void testCreate() throws Exception {
+        String javaPath = new File(System.getProperty("java.home"), ScriptUtils.isWindows() ? "bin\\java.exe" : "bin/java").getCanonicalPath();
+        System.err.println(javaPath);
+        StringBuilder command = new StringBuilder();
+        command.append(javaPath);
+        command.append(" -Dprop=\"key\"");
+        command.append(" -classpath ");
+        String clRes = getClass().getName().replace('.', '/') + ".class";
+        String str = getClass().getClassLoader().getResource(clRes).toString();
+        str = str.substring("file:".length(), str.indexOf(clRes));
+        command.append(str);
+        command.append(" ");
+        command.append(MainTest.class.getName());
+        command.append(" ");
+        command.append(60000);
+        System.err.println("Executing: " + command.toString());
+
+        ProcessBuilder builder = ProcessBuilderFactory.newInstance().newBuilder();
+        Process p = builder.command(command.toString()).start();
+        assertNotNull(p);
+        System.err.println("Process: " + p.getPid());
+        assertNotNull(p.getPid());
+        Thread.currentThread().sleep(1000);
+        System.err.println("Running: " + p.isRunning());
+        assertTrue(p.isRunning());
+        System.err.println("Destroying");
+        p.destroy();
+        Thread.currentThread().sleep(1000);
+        System.err.println("Running: " + p.isRunning());
+        assertFalse(p.isRunning());
+    }
+
+    /*
+     * When the process creation fails, no error is reported by the script
+     * 
+    public void testFailure() throws Exception {
+        ProcessBuilder builder = ProcessBuilderFactory.newInstance().newBuilder();
+        Process p = builder.command("ec").start();
+        fail("An exception should have been thrown");
+    }
+    */
+}
diff --git a/karaf/gshell/gshell-config/pom.xml b/karaf/gshell/gshell-config/pom.xml
new file mode 100644
index 0000000..8bab513
--- /dev/null
+++ b/karaf/gshell/gshell-config/pom.xml
@@ -0,0 +1,93 @@
+<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">
+
+    <!--
+
+        Licensed to the Apache Software Foundation (ASF) under one or more
+        contributor license agreements.  See the NOTICE file distributed with
+        this work for additional information regarding copyright ownership.
+        The ASF licenses this file to You under the Apache License, Version 2.0
+        (the "License"); you may not use this file except in compliance with
+        the License.  You may obtain a copy of the License at
+
+           http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.servicemix.kernel.gshell</groupId>
+        <artifactId>gshell</artifactId>
+        <version>1.2.0-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.apache.servicemix.kernel.gshell</groupId>
+    <artifactId>org.apache.servicemix.kernel.gshell.config</artifactId>
+    <packaging>bundle</packaging>
+    <version>1.2.0-SNAPSHOT</version>
+    <name>Apache ServiceMix Kernel :: GShell ConfigAdmin Commands</name>
+
+    <description>
+        Provides the ConfigAdmin GShell commands
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.servicemix.kernel.gshell</groupId>
+            <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.osgi</groupId>
+            <artifactId>spring-osgi-core</artifactId>
+        </dependency>
+
+        <dependency>
+             <groupId>org.apache.geronimo.specs</groupId>
+             <artifactId>geronimo-annotation_1.0_spec</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+                        <Export-Package>
+                            org.apache.servicemix.kernel.gshell.config*;version=${project.version};-split-package:=merge-first
+                        </Export-Package>
+                        <Import-Package>
+                            org.apache.geronimo.gshell.wisdom.command,
+                            org.apache.geronimo.gshell.wisdom.registry,
+                            org.apache.servicemix.kernel.gshell.core,
+                            *
+                        </Import-Package>
+                        <Private-Package>!*</Private-Package>
+                        <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/CancelCommand.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/CancelCommand.java
new file mode 100644
index 0000000..e31ccfb
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/CancelCommand.java
@@ -0,0 +1,28 @@
+/*
+ * 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.servicemix.kernel.gshell.config;
+
+import org.osgi.service.cm.ConfigurationAdmin;
+
+public class CancelCommand extends ConfigCommandSupport {
+
+    protected void doExecute(ConfigurationAdmin admin) throws Exception {
+        this.variables.parent().unset(PROPERTY_CONFIG_PID);
+        this.variables.parent().unset(PROPERTY_CONFIG_PROPS);
+    }
+
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/ConfigCommandSupport.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/ConfigCommandSupport.java
new file mode 100644
index 0000000..5a28289
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/ConfigCommandSupport.java
@@ -0,0 +1,64 @@
+/*
+ * 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.servicemix.kernel.gshell.config;
+
+import java.util.Dictionary;
+
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+/**
+ * Abstract class from which all commands related to the ConfigurationAdmin
+ * service should derive.
+ * This command retrieves a reference to the ConfigurationAdmin service before
+ * calling another method to actually process the command.
+ */
+public abstract class ConfigCommandSupport extends OsgiCommandSupport {
+
+    public static final String PROPERTY_CONFIG_PID = "ConfigCommand.PID";
+    public static final String PROPERTY_CONFIG_PROPS = "ConfigCommand.Props";
+
+    protected Object doExecute() throws Exception {
+        // Get config admin service.
+        ServiceReference ref = getBundleContext().getServiceReference(ConfigurationAdmin.class.getName());
+        if (ref == null) {
+            io.out.println("ConfigurationAdmin service is unavailable.");
+            return null;
+        }
+        try {
+            ConfigurationAdmin admin = (ConfigurationAdmin) getBundleContext().getService(ref);
+            if (admin == null) {
+                io.out.println("ConfigAdmin service is unavailable.");
+                return null;
+            }
+
+            doExecute(admin);
+        }
+        finally {
+            getBundleContext().ungetService(ref);
+        }
+        return null;
+    }
+
+    protected Dictionary getEditedProps() throws Exception {
+        return (Dictionary) this.variables.parent().get(PROPERTY_CONFIG_PROPS);
+    }
+
+    protected abstract void doExecute(ConfigurationAdmin admin) throws Exception;
+
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/EditCommand.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/EditCommand.java
new file mode 100644
index 0000000..f041da3
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/EditCommand.java
@@ -0,0 +1,43 @@
+/*
+ * 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.servicemix.kernel.gshell.config;
+
+import java.util.Dictionary;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+public class EditCommand extends ConfigCommandSupport {
+
+    @Argument(required = true, description = "PID of the configuration")
+    String pid;
+
+    @Option(name = "--force", description = "Force the edition of this config, even if another one was under edition")
+    boolean force;
+
+    protected void doExecute(ConfigurationAdmin admin) throws Exception {
+        String oldPid = (String) this.variables.get(PROPERTY_CONFIG_PID);
+        if (oldPid != null && !oldPid.equals(pid) && !force) {
+            io.err.println("Another config is being edited.  Cancel / update first, or use the --force option");
+            return;
+        }
+        Dictionary props = admin.getConfiguration(pid).getProperties();
+        this.variables.parent().set(PROPERTY_CONFIG_PID, pid);
+        this.variables.parent().set(PROPERTY_CONFIG_PROPS, props);
+    }
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/ListCommand.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/ListCommand.java
new file mode 100644
index 0000000..7657d6b
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/ListCommand.java
@@ -0,0 +1,50 @@
+/*
+ * 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.servicemix.kernel.gshell.config;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+public class ListCommand extends ConfigCommandSupport {
+
+    @Argument(required = false, description = "LDAP query")
+    String query;
+
+    protected void doExecute(ConfigurationAdmin admin) throws Exception {
+        Configuration[] configs = admin.listConfigurations(query);
+        for (Configuration config : configs) {
+            io.out.println("----------------------------------------------------------------");
+            io.out.println("Pid:            " + config.getPid());
+            if (config.getFactoryPid() != null) {
+                io.out.println("FactoryPid:     " + config.getFactoryPid());
+            }
+            io.out.println("BundleLocation: " + config.getBundleLocation());
+            if (config.getProperties() != null) {
+                io.out.println("Properties:");
+                Dictionary props = config.getProperties();
+                for (Enumeration e = props.keys(); e.hasMoreElements();) {
+                    Object key = e.nextElement();
+                    io.out.println("   " + key + " = " + props.get(key));
+                }
+            }
+        }
+    }
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropDelCommand.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropDelCommand.java
new file mode 100644
index 0000000..8e87dc7
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropDelCommand.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.config;
+
+import java.util.Dictionary;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+public class PropDelCommand extends ConfigCommandSupport {
+
+    @Argument(index = 0, required = true, description = "the property to delete")
+    String prop;
+
+    protected void doExecute(ConfigurationAdmin admin) throws Exception {
+        Dictionary props = getEditedProps();
+        if (props == null) {
+            System.err.println("No configuration is being edited. Run the edit command first");
+        } else {
+            props.remove(prop);
+        }
+    }
+
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropListCommand.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropListCommand.java
new file mode 100644
index 0000000..2f7a446
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropListCommand.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.config;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+
+import org.osgi.service.cm.ConfigurationAdmin;
+
+public class PropListCommand extends ConfigCommandSupport {
+
+    protected void doExecute(ConfigurationAdmin admin) throws Exception {
+        Dictionary props = getEditedProps();
+        if (props == null) {
+            System.err.println("No configuration is being edited. Run the edit command first");
+        } else {
+            for (Enumeration e = props.keys(); e.hasMoreElements();) {
+                Object key = e.nextElement();
+                io.out.println("   " + key + " = " + props.get(key));
+            }
+        }
+    }
+
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropSetCommand.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropSetCommand.java
new file mode 100644
index 0000000..a0c3112
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropSetCommand.java
@@ -0,0 +1,41 @@
+/*
+ * 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.servicemix.kernel.gshell.config;
+
+import java.util.Dictionary;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+public class PropSetCommand extends ConfigCommandSupport {
+
+    @Argument(index = 0, required = true, description = "the property to set")
+    String prop;
+
+    @Argument(index = 1, required = true, description = "the value of the property")
+    String value;
+
+    protected void doExecute(ConfigurationAdmin admin) throws Exception {
+        Dictionary props = getEditedProps();
+        if (props == null) {
+            System.err.println("No configuration is being edited. Run the edit command first");
+        } else {
+            props.put(prop, value);
+        }
+    }
+
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/UpdateCommand.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/UpdateCommand.java
new file mode 100644
index 0000000..1c6683f
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/UpdateCommand.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.config;
+
+import java.util.Dictionary;
+
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+public class UpdateCommand extends ConfigCommandSupport {
+
+    protected void doExecute(ConfigurationAdmin admin) throws Exception {
+        Dictionary props = getEditedProps();
+        if (props == null) {
+            System.err.println("No configuration is being edited. Run the edit command first");
+        } else {
+            String pid = (String) this.variables.parent().get(PROPERTY_CONFIG_PID);
+            Configuration cfg = admin.getConfiguration(pid, null);
+            cfg.update(props);
+            this.variables.parent().unset(PROPERTY_CONFIG_PID);
+            this.variables.parent().unset(PROPERTY_CONFIG_PROPS);
+        }
+    }
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/completers/ConfigurationCompleter.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/completers/ConfigurationCompleter.java
new file mode 100644
index 0000000..7b63351
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/completers/ConfigurationCompleter.java
@@ -0,0 +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.
+ */
+
+package org.apache.servicemix.kernel.gshell.config.completers;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+
+import jline.Completor;
+import org.apache.geronimo.gshell.console.completer.StringsCompleter;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.cm.ConfigurationEvent;
+import org.osgi.service.cm.ConfigurationListener;
+
+/**
+ * {@link jline.Completor} for Configuration Admin configurations.
+ *
+ * Displays a list of existing config admin configurations for completion.
+ *
+ */
+public class ConfigurationCompleter implements Completor, ConfigurationListener {
+
+    private final StringsCompleter delegate = new StringsCompleter();
+
+    private ConfigurationAdmin admin;
+
+    public void setAdmin(ConfigurationAdmin admin) {
+        this.admin = admin;
+    }
+
+    @PostConstruct
+    public void init() {
+        Configuration[] configs;
+        try {
+            configs = admin.listConfigurations(null);
+        } catch (Exception e) {
+            return;
+        }
+
+        Collection<String> pids = new ArrayList<String>();
+
+        for (Configuration config : configs) {
+            if (config.getFactoryPid() != null) {
+                pids.add(config.getFactoryPid());
+            } else {
+                pids.add(config.getPid());
+            }
+        }
+
+        delegate.getStrings().addAll(pids);
+
+    }
+
+    public int complete(final String buffer, final int cursor, final List candidates) {
+        return delegate.complete(buffer, cursor, candidates);
+    }
+
+    public void configurationEvent(ConfigurationEvent configurationEvent) {
+        String pid = configurationEvent.getFactoryPid()!=null ? configurationEvent.getFactoryPid() : configurationEvent.getPid();
+        if (configurationEvent.getType() == ConfigurationEvent.CM_DELETED) {
+            delegate.getStrings().remove(pid);
+        } else if (configurationEvent.getType() == ConfigurationEvent.CM_UPDATED) {
+            delegate.getStrings().add(pid);
+        }
+    }
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/completers/ConfigurationPropertyCompleter.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/completers/ConfigurationPropertyCompleter.java
new file mode 100644
index 0000000..e21a8e3
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/completers/ConfigurationPropertyCompleter.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.servicemix.kernel.gshell.config.completers;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.List;
+
+import jline.Completor;
+import org.apache.geronimo.gshell.command.Variables;
+import org.apache.geronimo.gshell.console.completer.StringsCompleter;
+import org.apache.geronimo.gshell.shell.ShellContextHolder;
+import org.apache.servicemix.kernel.gshell.config.ConfigCommandSupport;
+
+/**
+ * {@link jline.Completor} for Configuration Admin properties.
+ *
+ * Displays a list of existing properties based on the current configuration being edited.
+ *
+ */
+public class ConfigurationPropertyCompleter implements Completor {
+
+    public int complete(final String buffer, final int cursor, final List candidates) {
+        Variables vars = ShellContextHolder.get().getVariables();
+        if (vars.get(ConfigCommandSupport.PROPERTY_CONFIG_PID) == null) {
+            return -1;
+        }
+
+        Dictionary props = (Dictionary) vars.get(ConfigCommandSupport.PROPERTY_CONFIG_PROPS);
+        StringsCompleter delegate = new StringsCompleter();
+
+        for (Enumeration e = props.keys(); e.hasMoreElements();) {
+            String key = (String) e.nextElement();
+            delegate.getStrings().add(key);
+        }
+
+        return delegate.complete(buffer, cursor, candidates);
+    }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-config/src/main/resources/META-INF/spring/gshell-config.xml b/karaf/gshell/gshell-config/src/main/resources/META-INF/spring/gshell-config.xml
new file mode 100644
index 0000000..e5c9d6d
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/resources/META-INF/spring/gshell-config.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:osgi="http://www.springframework.org/schema/osgi"
+       xmlns:osgix="http://www.springframework.org/schema/osgi-compendium"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+       xsi:schemaLocation="
+  http://www.springframework.org/schema/beans
+  http://www.springframework.org/schema/beans/spring-beans.xsd
+  http://www.springframework.org/schema/osgi
+  http://www.springframework.org/schema/osgi/spring-osgi.xsd
+  http://www.springframework.org/schema/osgi-compendium
+  http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd
+  http://www.springframework.org/schema/util
+  http://www.springframework.org/schema/util/spring-util.xsd
+  http://servicemix.apache.org/schema/servicemix-gshell
+  http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+    <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+    <gshell:command-bundle>
+        <gshell:command name="config/cancel">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.config.CancelCommand" />
+        </gshell:command>
+        <gshell:command name="config/edit">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.config.EditCommand"/>
+            <gshell:completers>
+                <ref bean="configCompleter" />
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+        <gshell:command name="config/list">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.config.ListCommand" />
+        </gshell:command>
+        <gshell:command name="config/propdel">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.config.PropDelCommand" />
+            <gshell:completers>
+                <ref bean="configPropertyCompleter" />
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+        <gshell:command name="config/proplist">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.config.PropListCommand" />
+        </gshell:command>
+        <gshell:command name="config/propset">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.config.PropSetCommand" />
+            <gshell:completers>
+                <ref bean="configPropertyCompleter" />
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+        <gshell:command name="config/update">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.config.UpdateCommand" />
+        </gshell:command>
+    </gshell:command-bundle>
+
+    <bean id="configCompleter" class="org.apache.servicemix.kernel.gshell.config.completers.ConfigurationCompleter" init-method="init">
+        <property name="admin" ref="configAdmin"/>
+    </bean>
+
+    <bean id="configPropertyCompleter" class="org.apache.servicemix.kernel.gshell.config.completers.ConfigurationPropertyCompleter" />
+
+    <osgi:reference id="configAdmin" interface="org.osgi.service.cm.ConfigurationAdmin"  />
+
+    <osgi:service ref="configCompleter" interface="org.osgi.service.cm.ConfigurationListener" /> 
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/CancelCommand.properties b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/CancelCommand.properties
new file mode 100644
index 0000000..1bc5226
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/CancelCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Change the changes to the configuration being edited.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/EditCommand.properties b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/EditCommand.properties
new file mode 100644
index 0000000..ca2f8e6
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/EditCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Create or edit a configuration.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/ListCommand.properties b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/ListCommand.properties
new file mode 100644
index 0000000..a5d2c31
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/ListCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=List existing configurations.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropDelCommand.properties b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropDelCommand.properties
new file mode 100644
index 0000000..9e76af6
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropDelCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Delete a property from the edited configuration.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropListCommand.properties b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropListCommand.properties
new file mode 100644
index 0000000..9dab240
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropListCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=List properties from the edited configuration.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropSetCommand.properties b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropSetCommand.properties
new file mode 100644
index 0000000..df4a2dc
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropSetCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Set a property on the edited configuration.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/UpdateCommand.properties b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/UpdateCommand.properties
new file mode 100644
index 0000000..810f08f
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/UpdateCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Save and propagate changes from the configuration being edited.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/pom.xml b/karaf/gshell/gshell-core/pom.xml
new file mode 100644
index 0000000..19e9a27
--- /dev/null
+++ b/karaf/gshell/gshell-core/pom.xml
@@ -0,0 +1,480 @@
+<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">
+
+    <!--
+
+        Licensed to the Apache Software Foundation (ASF) under one or more
+        contributor license agreements.  See the NOTICE file distributed with
+        this work for additional information regarding copyright ownership.
+        The ASF licenses this file to You under the Apache License, Version 2.0
+        (the "License"); you may not use this file except in compliance with
+        the License.  You may obtain a copy of the License at
+
+           http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.servicemix.kernel.gshell</groupId>
+        <artifactId>gshell</artifactId>
+        <version>1.2.0-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.apache.servicemix.kernel.gshell</groupId>
+    <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+    <packaging>bundle</packaging>
+    <version>1.2.0-SNAPSHOT</version>
+    <name>Apache ServiceMix Kernel :: GShell Core</name>
+
+    <description>
+        Provides the OSGi GShell integration
+    </description>
+
+    <properties>
+        <gshell.osgi.import>
+            org.springframework.aop,
+            org.springframework.aop.framework,
+            org.springframework.beans.factory.annotation,
+            org.springframework.context.annotation,
+            org.springframework.osgi.service.importer,
+            org.aopalliance.aop,
+            org.apache.commons.vfs.provider.temp,
+            org.apache.commons.vfs.provider.ram,
+            jline*,
+            org.apache.servicemix.kernel.jaas.config;resolution:=optional,
+            org.apache.servicemix.kernel.version;resolution:=optional, 
+            org.apache.servicemix.kernel.main.spi;resolution:=optional;version="1.0.0",
+            org.codehaus.plexus*;resolution:=optional,
+            org.apache.sshd.server.keyprovider,
+            org.apache.sshd.server.jaas,
+            org.jsecurity*;resolution:=optional,
+            *
+        </gshell.osgi.import>
+        <!-- TODO: remove plexus util package -->
+        <gshell.osgi.export>
+            org.apache.geronimo.gshell*;version="1.0.0.alpha-2-SNAPSHOT";-split-package:=merge-first,
+            org.apache.servicemix.kernel.gshell.core*,
+            org.codehaus.plexus.interpolation*;-split-package:=merge-first,
+            org.codehaus.plexus.util;-split-package:=merge-first
+        </gshell.osgi.export>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.servicemix.kernel</groupId>
+            <artifactId>org.apache.servicemix.kernel.main</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.kernel.jaas</groupId>
+            <artifactId>org.apache.servicemix.kernel.jaas.config</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.cglib</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.osgi</groupId>
+            <artifactId>spring-osgi-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.geronimo.gshell.commands</groupId>
+            <artifactId>gshell-builtin</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>oro</groupId>
+                    <artifactId>oro</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>commons-vfs</groupId>
+                    <artifactId>commons-vfs</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.geronimo.gshell.commands</groupId>
+            <artifactId>gshell-file</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.geronimo.gshell.commands</groupId>
+            <artifactId>gshell-network</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.geronimo.gshell.commands</groupId>
+            <artifactId>gshell-shell</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.geronimo.gshell.commands</groupId>
+            <artifactId>gshell-ssh</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.geronimo.gshell.commands</groupId>
+            <artifactId>gshell-text</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.geronimo.gshell.wisdom</groupId>
+            <artifactId>gshell-wisdom-core</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.geronimo.gshell.support</groupId>
+                    <artifactId>gshell-ivy</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.geronimo.gshell.support</groupId>
+                    <artifactId>gshell-xstore</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>commons-jexl</groupId>
+                    <artifactId>commons-jexl</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.plexus</groupId>
+            <artifactId>plexus-utils</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-aop</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-jdk14</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.commons-httpclient</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>commons-codec</groupId>
+                    <artifactId>commons-codec</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.commons-codec</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.commons-jexl</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>junit</groupId>
+                    <artifactId>junit</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.commons-vfs</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.oro</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.mina</groupId>
+            <artifactId>mina-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.jline</artifactId>
+        </dependency>
+        
+        <!-- jsecurity is a dependency for the ssh commands -->
+        <dependency>
+            <groupId>org.jsecurity</groupId>
+            <artifactId>jsecurity</artifactId>
+            <version>0.9.0-RC2</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>commons-logging</groupId>
+                    <artifactId>commons-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>${pom.basedir}/src/main/resources</directory>
+                <includes>
+                    <include>**/*</include>
+                </includes>
+            </resource>
+            <resource>
+                <directory>${pom.basedir}/src/main/filtered-resources</directory>
+                <filtering>true</filtering>
+                <includes>
+                    <include>**/*</include>
+                </includes>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>Main</mainClass>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+                        <Import-Package>${gshell.osgi.import}</Import-Package>
+                        <Export-Package>${gshell.osgi.export}</Export-Package>
+                        <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+                    </instructions>
+                    <unpackBundle>true</unpackBundle>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <artifactSet>
+                                <includes>
+                                    <include>org.apache.geronimo.gshell:gshell-api</include>
+                                    <include>org.apache.geronimo.gshell:gshell-application</include>
+                                    <include>org.apache.geronimo.gshell:gshell-parser</include>
+                                    <include>org.apache.geronimo.gshell.commands:gshell-builtin</include>
+                                    <include>org.apache.geronimo.gshell.commands:gshell-file</include>
+                                    <include>org.apache.geronimo.gshell.commands:gshell-network</include>
+                                    <include>org.apache.geronimo.gshell.commands:gshell-shell</include>
+                                    <include>org.apache.geronimo.gshell.commands:gshell-text</include>
+                                    <include>org.apache.geronimo.gshell.support:gshell-ansi</include>
+                                    <include>org.apache.geronimo.gshell.support:gshell-artifact</include>
+                                    <include>org.apache.geronimo.gshell.support:gshell-chronos</include>
+                                    <include>org.apache.geronimo.gshell.support:gshell-clp</include>
+                                    <include>org.apache.geronimo.gshell.support:gshell-console</include>
+                                    <include>org.apache.geronimo.gshell.support:gshell-event</include>
+                                    <include>org.apache.geronimo.gshell.support:gshell-i18n</include>
+                                    <include>org.apache.geronimo.gshell.support:gshell-interpolation</include>
+                                    <include>org.apache.geronimo.gshell.support:gshell-io</include>
+                                    <include>org.apache.geronimo.gshell.support:gshell-security</include>
+                                    <include>org.apache.geronimo.gshell.support:gshell-spring</include>
+                                    <include>org.apache.geronimo.gshell.support:gshell-terminal</include>
+                                    <include>org.apache.geronimo.gshell.support:gshell-vfs</include>
+                                    <include>org.apache.geronimo.gshell.support:gshell-vfs-meta</include>
+                                    <include>org.apache.geronimo.gshell.support:gshell-yarn</include>
+                                    <include>org.apache.geronimo.gshell.wisdom:gshell-wisdom-core</include>
+                                    <include>org.apache.geronimo.gshell.wisdom:gshell-wisdom-bootstrap</include>
+                                    <include>org.codehaus.plexus:plexus-utils</include>
+                                    <include>org.codehaus.plexus:plexus-interpolation</include>
+                                    <include>${project.groupId}:${project.artifactId}</include>
+                                </includes>
+                            </artifactSet>
+                            <filters>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell:gshell-api</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell:gshell-application</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell:gshell-parser</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell.commands:gshell-builtin</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell.commands:gshell-file</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell.commands:gshell-network</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell.commands:gshell-shell</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell.commands:gshell-ssh</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell.commands:gshell-text</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell.support:gshell-ansi</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell.support:gshell-artifact</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell.support:gshell-chronos</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell.support:gshell-clp</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell.support:gshell-console</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell.support:gshell-event</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell.support:gshell-i18n</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell.support:gshell-interpolation</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell.support:gshell-io</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell.support:gshell-security</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell.support:gshell-spring</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell.support:gshell-terminal</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell.support:gshell-vfs</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell.support:gshell-vfs-meta</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell.support:gshell-yarn</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell.wisdom:gshell-wisdom-core</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.apache.geronimo.gshell.wisdom:gshell-wisdom-bootstrap</artifact>
+                                    <excludes>
+                                        <exclude>org/apache/geronimo/gshell/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.codehaus.plexus:plexus-utils</artifact>
+                                    <excludes>
+                                        <exclude>org/codehaus/plexus/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.codehaus.plexus:plexus-interpolation</artifact>
+                                    <excludes>
+                                        <exclude>org/codehaus/plexus/**</exclude>
+                                    </excludes>
+                                </filter>
+                            </filters>
+                            <createSourcesJar>${createSourcesJar}</createSourcesJar>
+                            <promoteTransitiveDependencies>true</promoteTransitiveDependencies>
+                            <createDependencyReducedPom>true</createDependencyReducedPom>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/karaf/gshell/gshell-core/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/core/servicemix-version.properties b/karaf/gshell/gshell-core/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/core/servicemix-version.properties
new file mode 100644
index 0000000..1fc0355
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/core/servicemix-version.properties
@@ -0,0 +1,20 @@
+##
+## 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.
+##
+
+version=${pom.version}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/shell/FindAction.java b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/shell/FindAction.java
new file mode 100644
index 0000000..203e340
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/shell/FindAction.java
@@ -0,0 +1,208 @@
+/*
+ * 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.geronimo.gshell.commands.shell;
+
+import java.net.URI;
+
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSelectInfo;
+import org.apache.commons.vfs.FileSelector;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.FileType;
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.geronimo.gshell.command.CommandAction;
+import org.apache.geronimo.gshell.command.CommandContext;
+import org.apache.geronimo.gshell.io.IO;
+import org.apache.geronimo.gshell.vfs.FileObjects;
+import org.apache.geronimo.gshell.vfs.selector.AggregateFileSelector;
+import org.apache.geronimo.gshell.vfs.support.VfsActionSupport;
+import org.apache.oro.text.GlobCompiler;
+import org.apache.oro.text.regex.MalformedPatternException;
+import org.apache.oro.text.regex.Pattern;
+import org.apache.oro.text.regex.PatternCompiler;
+import org.apache.oro.text.regex.PatternMatcher;
+import org.apache.oro.text.regex.Perl5Matcher;
+
+/**
+ * Find files in a hierarchy.
+ *
+ * TODO: remove this file when gshell is upgraded
+ *
+ * @version $Rev: 722797 $ $Date: 2008-12-03 08:18:16 +0100 (Wed, 03 Dec 2008) $
+ */
+public class FindAction
+    extends VfsActionSupport
+{
+    private final AggregateFileSelector selector = new AggregateFileSelector();
+
+    @Option(name="-name")
+    private void addNameFilter(final String name) throws MalformedPatternException {
+        log.debug("Adding -name selector for: {}", name);
+        selector.getSelectors().add(new NameSelector(name));
+    }
+
+    @Option(name="-iname")
+    private void addiNameFilter(final String name) throws MalformedPatternException {
+        log.debug("Adding -iname selector for: {}", name);
+        selector.getSelectors().add(new NameSelector(name, true));
+    }
+
+    @Option(name="-type")
+    private void addTypeFilter(final Type type) {
+        log.debug("Adding -type selector for: {}", type);
+        selector.getSelectors().add(new TypeSelector(type));
+    }
+
+    @Argument(required=true)
+    private String path;
+
+    public Object execute(final CommandContext context) throws Exception {
+        assert context != null;
+        IO io = context.getIo();
+
+        FileObject root = resolveFile(context, path);
+
+        ensureFileExists(root);
+
+        find(context, root, selector);
+
+        FileObjects.close(root);
+
+        return CommandAction.Result.SUCCESS;
+    }
+
+    private void find(final CommandContext context, final FileObject file, final FileSelector selector) throws FileSystemException {
+        assert context != null;
+        assert file != null;
+        assert selector != null;
+
+        FileObject[] files = file.findFiles(selector);
+
+        if (files != null && files.length != 0) {
+            for (FileObject child : files) {
+                display(context, child, file);
+            }
+        }
+    }
+
+    private void display(final CommandContext context, final FileObject file, final FileObject root) throws FileSystemException {
+        assert context != null;
+        assert file != null;
+
+        String path;
+        try {
+            path = new URI(this.path).resolve(root.getURL().toURI().relativize(file.getURL().toURI())).toString();
+        } catch (Exception e) {
+            path = file.getName().getPath();
+        }
+        IO io = context.getIo();
+        io.info(path);
+    }
+
+    //
+    // Type & TypeSelector
+    //
+
+    private enum Type
+    {
+        F, // normal file
+        D, // directory
+    }
+
+    private class TypeSelector
+        implements FileSelector
+    {
+        private final Type type;
+
+        public TypeSelector(final Type type) {
+            assert type != null;
+
+            this.type = type;
+
+            log.trace("Type: {}", type);
+        }
+
+        public boolean includeFile(final FileSelectInfo selection) throws Exception {
+            assert selection != null;
+
+            FileType ftype = selection.getFile().getType();
+
+            switch (type) {
+                case D:
+                    return ftype == FileType.FOLDER;
+
+                case F:
+                    return ftype == FileType.FILE;
+
+                // TODO: Handle FileType.FILE_OR_FOLDER
+
+                default:
+                    return false;
+            }
+        }
+
+        public boolean traverseDescendents(final FileSelectInfo selection) throws Exception {
+            return true;
+        }
+    }
+
+    //
+    // NameSelector
+    //
+
+    private class NameSelector
+        implements FileSelector
+    {
+        private final Pattern pattern;
+
+        private final PatternMatcher matcher;
+
+        public NameSelector(final String name, final boolean ignoreCase) throws MalformedPatternException {
+            assert name != null;
+
+            PatternCompiler compiler = new GlobCompiler();
+            int options;
+            if (ignoreCase) {
+                options = GlobCompiler.CASE_INSENSITIVE_MASK;
+            }
+            else {
+                options = GlobCompiler.DEFAULT_MASK;
+            }
+            this.pattern = compiler.compile(name, options);
+            this.matcher = new Perl5Matcher();
+
+            log.trace("Pattern: {}", pattern.getPattern());
+        }
+
+        public NameSelector(final String name) throws MalformedPatternException {
+            this(name, false);
+        }
+
+        public boolean includeFile(final FileSelectInfo selection) throws Exception {
+            assert selection != null;
+            return matcher.matches(selection.getFile().getName().getBaseName(), pattern);
+        }
+
+        public boolean traverseDescendents(final FileSelectInfo selection) throws Exception {
+            return true;
+        }
+    }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/JSecurityPasswordAuthenticator.java b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/JSecurityPasswordAuthenticator.java
new file mode 100644
index 0000000..d3bb6de
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/JSecurityPasswordAuthenticator.java
@@ -0,0 +1,84 @@
+/*
+ * 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.geronimo.gshell.commands.ssh;
+
+import org.apache.sshd.server.PasswordAuthenticator;
+import org.jsecurity.SecurityUtils;
+import org.jsecurity.authc.AuthenticationException;
+import org.jsecurity.authc.UsernamePasswordToken;
+import org.jsecurity.mgt.SecurityManager;
+import org.jsecurity.subject.Subject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * <a href="http://jsecurity.org">JSecurity</a> {@link PasswordAuthenticator}.
+ *
+ * @version $Rev: 722797 $ $Date: 2008-12-03 08:18:16 +0100 (Wed, 03 Dec 2008) $
+ */
+public class JSecurityPasswordAuthenticator
+    implements PasswordAuthenticator
+{
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private final SecurityManager securityManager;
+
+    public JSecurityPasswordAuthenticator(final SecurityManager securityManager) {
+        // securityManager can be null
+        this.securityManager = securityManager;
+    }
+
+    public JSecurityPasswordAuthenticator() {
+        this(null);
+    }
+
+    public Object authenticate(final String username, final String password) {
+        assert username != null;
+        assert password != null;
+
+        log.debug("Authenticating: {}/{}", username, password);
+
+        Subject currentUser;
+
+        if (securityManager != null) {
+            currentUser = securityManager.getSubject();
+        }
+        else {
+            currentUser = SecurityUtils.getSubject();
+        }
+
+        if (currentUser.isAuthenticated()) {
+            log.debug("Logging out current user: {}", currentUser.getPrincipal());
+            currentUser.logout();
+        }
+
+        try {
+            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
+            currentUser.login(token);
+            Object principal = currentUser.getPrincipal();
+            log.info("User [{}] logged in successfully", principal);
+            return principal;
+        }
+        catch (AuthenticationException e) {
+            log.error("Authentication failed: " + e, e);
+            return null;
+        }
+    }
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/ShellFactoryImpl.java b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/ShellFactoryImpl.java
new file mode 100644
index 0000000..9795f01
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/ShellFactoryImpl.java
@@ -0,0 +1,267 @@
+/*
+ * 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.geronimo.gshell.commands.ssh;
+
+import org.apache.sshd.server.ShellFactory;
+import jline.Completor;
+import jline.History;
+import org.apache.geronimo.gshell.command.Variables;
+import org.apache.geronimo.gshell.commandline.CommandLineExecutor;
+import org.apache.geronimo.gshell.console.Console;
+import org.apache.geronimo.gshell.console.JLineConsole;
+import org.apache.geronimo.gshell.console.completer.AggregateCompleter;
+import org.apache.geronimo.gshell.io.Closer;
+import org.apache.geronimo.gshell.io.IO;
+import org.apache.geronimo.gshell.notification.ExitNotification;
+import org.apache.geronimo.gshell.shell.ShellContext;
+import org.apache.geronimo.gshell.shell.ShellContextHolder;
+import org.apache.geronimo.gshell.registry.CommandResolver;
+import org.apache.geronimo.gshell.application.Application;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * SSHD {@link ShellFactory} which provides access to GShell.
+ *
+ * @version $Rev: 731517 $ $Date: 2009-01-05 11:25:19 +0100 (Mon, 05 Jan 2009) $
+ */
+public class ShellFactoryImpl
+    implements ShellFactory
+{
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private Application application;
+
+    private Console.Prompter prompter;
+
+    private CommandLineExecutor executor;
+
+    private History history;
+
+    private List<Completor> completers;
+
+    private Console.ErrorHandler errorHandler;
+
+    public Console.Prompter getPrompter() {
+        return prompter;
+    }
+
+    public void setPrompter(final Console.Prompter prompter) {
+        this.prompter = prompter;
+    }
+
+    public CommandLineExecutor getExecutor() {
+        return executor;
+    }
+
+    public void setExecutor(final CommandLineExecutor executor) {
+        this.executor = executor;
+    }
+
+    public History getHistory() {
+        return history;
+    }
+
+    public void setHistory(final History history) {
+        this.history = history;
+    }
+
+    public List<Completor> getCompleters() {
+        return completers;
+    }
+
+    public void setCompleters(final List<Completor> completers) {
+        this.completers = completers;
+    }
+
+    public Console.ErrorHandler getErrorHandler() {
+        return errorHandler;
+    }
+
+    public void setErrorHandler(final Console.ErrorHandler errorHandler) {
+        this.errorHandler = errorHandler;
+    }
+
+    public Application getApplication() {
+        return application;
+    }
+
+    public void setApplication(Application application) {
+        this.application = application;
+    }
+
+    public Shell createShell() {
+        return new ShellImpl();
+    }
+
+    public class ShellImpl
+        implements ShellFactory.Shell, org.apache.geronimo.gshell.shell.Shell, ShellContext, Runnable
+    {
+        private InputStream in;
+
+        private OutputStream out;
+
+        private OutputStream err;
+
+        private ExitCallback callback;
+
+        private IO io;
+
+        private Variables variables;
+
+        private boolean closed;
+
+        public void setInputStream(final InputStream in) {
+            this.in = in;
+        }
+
+        public void setOutputStream(final OutputStream out) {
+            this.out = out;
+        }
+
+        public void setErrorStream(final OutputStream err) {
+            this.err = err;
+        }
+
+        public void setExitCallback(ExitCallback callback) {
+            this.callback = callback;
+        }
+
+        public void start(final Map<String,String> env) throws IOException {
+            this.io = new IO(in, out, err, false);
+
+            // Create variables, inheriting the application ones
+            this.variables = new Variables(application.getVariables());
+            // Set up additional env
+            if (env != null) {
+                for (Map.Entry<String,String> entry : env.entrySet()) {
+                    this.variables.set(entry.getKey(), entry.getValue());
+                }
+            }
+            this.variables.set("gshell.prompt", application.getModel().getBranding().getPrompt());
+            this.variables.set(CommandResolver.GROUP, "/");
+            this.variables.set("gshell.username", env.get("USER"));
+            this.variables.set("gshell.hostname", application.getLocalHost());
+            // HACK: Add history for the 'history' command, since its not part of the Shell intf it can't really access it
+            this.variables.set("gshell.internal.history", getHistory(), true);
+            new Thread(this).start();
+        }
+
+        public void destroy() {
+            close();
+        }
+
+        public ShellContext getContext() {
+            return this;
+        }
+
+        public Object execute(final String line) throws Exception {
+
+            return executor.execute(getContext(), line);
+        }
+
+        public Object execute(final String command, final Object[] args) throws Exception {
+            return executor.execute(getContext(), args);
+        }
+
+        public Object execute(final Object... args) throws Exception {
+            return executor.execute(getContext(), args);
+        }
+
+        public boolean isOpened() {
+            return !closed;
+        }
+
+        public void close() {
+            closed = true;
+            Closer.close(in, out, err);
+            callback.onExit(0);
+        }
+
+        public boolean isInteractive() {
+            return false;
+        }
+
+        public void run(final Object... args) throws Exception {
+            Console.Executor executor = new Console.Executor()
+            {
+                public Result execute(final String line) throws Exception {
+                    assert line != null;
+                    try {
+                        ShellImpl.this.execute(line);
+                    }
+                    catch (ExitNotification n) {
+                        return Result.STOP;
+                    }
+                    return Result.CONTINUE;
+                }
+            };
+
+            IO io = getContext().getIo();
+
+            // Setup the console runner
+            JLineConsole console = new JLineConsole(executor, io);
+            console.setPrompter(getPrompter());
+            console.setErrorHandler(getErrorHandler());
+            console.setHistory(getHistory());
+
+            if (completers != null) {
+                // Have to use aggregate here to get the completion list to update properly
+                console.addCompleter(new AggregateCompleter(completers));
+            }
+            
+            console.run();
+        }
+
+        public org.apache.geronimo.gshell.shell.Shell getShell() {
+            return this;
+        }
+
+        public IO getIo() {
+            return io;
+        }
+
+        public Variables getVariables() {
+            return variables;
+        }
+
+        public void run() {
+            ShellContext ctx = ShellContextHolder.get(true);
+
+            try {
+                ShellContextHolder.set(getContext());
+                run(new Object[0]);
+            }
+            catch (Exception e) {
+                log.error("Unhandled failure: " + e, e);
+            }
+            finally {
+                ShellContextHolder.set(ctx);
+                close();
+            }
+        }
+    }
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/SshAction.java b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/SshAction.java
new file mode 100644
index 0000000..cafc437
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/SshAction.java
@@ -0,0 +1,169 @@
+/*
+ * 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.geronimo.gshell.commands.ssh;
+
+import org.apache.sshd.ClientChannel;
+import org.apache.sshd.ClientSession;
+import org.apache.sshd.SshClient;
+import org.apache.sshd.client.future.ConnectFuture;
+import org.apache.sshd.common.util.NoCloseInputStream;
+import org.apache.sshd.common.util.NoCloseOutputStream;
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.geronimo.gshell.command.CommandAction;
+import org.apache.geronimo.gshell.command.CommandContext;
+import org.apache.geronimo.gshell.i18n.MessageSource;
+import org.apache.geronimo.gshell.io.IO;
+import org.apache.geronimo.gshell.io.PromptReader;
+import org.apache.geronimo.gshell.spring.BeanContainer;
+import org.apache.geronimo.gshell.spring.BeanContainerAware;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Connect to a SSH server.
+ *
+ * @version $Rev: 721244 $ $Date: 2008-11-27 18:19:56 +0100 (Thu, 27 Nov 2008) $
+ */
+public class SshAction
+    implements CommandAction, BeanContainerAware
+{
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    @Option(name="-l", aliases={"--username"})
+    private String username;
+
+    @Option(name="-P", aliases={"--password"})
+    private String password;
+
+    @Argument(required=true)
+    private String hostname;
+
+    @Option(name="-p", aliases={"--port"})
+    private int port = 22;
+
+    private BeanContainer container;
+
+	private ClientSession session;
+
+    public void setBeanContainer(final BeanContainer container) {
+        assert container != null;
+        this.container = container;
+    }
+
+    /**
+     * Helper to validate that prompted username or password is not null or empty.
+     */
+    private class UsernamePasswordValidator
+        implements PromptReader.Validator
+    {
+        private String type;
+
+        private int count = 0;
+
+        private int max = 3;
+
+        public UsernamePasswordValidator(final String type) {
+            assert type != null;
+
+            this.type = type;
+        }
+
+        public boolean isValid(final String value) {
+            count++;
+
+            if (value != null && value.trim().length() > 0) {
+                return true;
+            }
+
+            if (count >= max) {
+                throw new RuntimeException("Too many attempts; failed to prompt user for " + type + " after " + max + " tries");
+            }
+
+            return false;
+        }
+    }
+
+    public Object execute(final CommandContext context) throws Exception {
+        assert context != null;
+        IO io = context.getIo();
+        MessageSource messages = context.getCommand().getMessages();
+
+        //
+        // TODO: Parse hostname for <username>@<hostname>
+        //
+        
+        io.info(messages.format("info.connecting", hostname, port));
+
+        // If the username/password was not configured via cli, then prompt the user for the values
+        if (username == null || password == null) {
+            PromptReader prompter = new PromptReader(io);
+            String text;
+
+            log.debug("Prompting user for credentials");
+
+            if (username == null) {
+                text = messages.getMessage("prompt.username");
+                username = prompter.readLine(text + ": ", new UsernamePasswordValidator(text));
+            }
+
+            if (password == null) {
+                text = messages.getMessage("prompt.password");
+                password = prompter.readPassword(text + ": ", new UsernamePasswordValidator(text));
+            }
+        }
+
+        // Create the client from prototype
+        SshClient client = container.getBean(SshClient.class);
+        log.debug("Created client: {}", client);
+        client.start();;
+
+        try {
+            ConnectFuture future = client.connect(hostname, port);
+            future.await();
+            session = future.getSession();
+            try {
+                io.info(messages.getMessage("info.connected"));
+
+                session.authPassword(username, password);
+                int ret = session.waitFor(ClientSession.WAIT_AUTH | ClientSession.CLOSED | ClientSession.AUTHED, 0);
+                if ((ret & ClientSession.AUTHED) == 0) {
+                    io.err.println("Authentication failed");
+                    return Result.FAILURE;
+                }
+
+                ClientChannel channel = session.createChannel("shell");
+                channel.setIn(new NoCloseInputStream(io.inputStream));
+                channel.setOut(new NoCloseOutputStream(io.outputStream));
+                channel.setErr(new NoCloseOutputStream(io.errorStream));
+                channel.open();
+                channel.waitFor(ClientChannel.CLOSED, 0);
+            } finally {
+                session.close(false);
+            }
+        } finally {
+            client.stop();
+        }
+
+        io.verbose(messages.getMessage("verbose.disconnected"));
+
+        return Result.SUCCESS;
+    }
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/SshServerAction.java b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/SshServerAction.java
new file mode 100644
index 0000000..db1cfb0
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/SshServerAction.java
@@ -0,0 +1,84 @@
+/*
+ * 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.geronimo.gshell.commands.ssh;
+
+import org.apache.sshd.SshServer;
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.geronimo.gshell.command.CommandAction;
+import org.apache.geronimo.gshell.command.CommandContext;
+import org.apache.geronimo.gshell.i18n.MessageSource;
+import org.apache.geronimo.gshell.io.IO;
+import org.apache.geronimo.gshell.spring.BeanContainer;
+import org.apache.geronimo.gshell.spring.BeanContainerAware;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Start a SSH server.
+ *
+ * @version $Rev: 720411 $ $Date: 2008-11-25 05:32:43 +0100 (Tue, 25 Nov 2008) $
+ */
+public class SshServerAction
+    implements CommandAction, BeanContainerAware
+{
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    @Option(name="-p", aliases={ "--port" })
+    private int port=8101;
+
+    @Option(name="-b", aliases={ "--background"})
+    private boolean background = true;
+
+    private BeanContainer container;
+
+    public void setBeanContainer(final BeanContainer container) {
+        assert container != null;
+
+        this.container = container;
+    }
+
+    public Object execute(final CommandContext context) throws Exception {
+        assert context != null;
+        IO io = context.getIo();
+        MessageSource messages = context.getCommand().getMessages();
+
+        SshServer server = container.getBean("sshServer", SshServer.class);
+
+        log.debug("Created server: {}", server);
+
+        server.setPort(port);
+
+        server.start();
+
+        io.info(messages.format("info.listening", port));
+
+        if (!background) {
+            synchronized (this) {
+                log.debug("Waiting for server to shutdown");
+
+                wait();
+            }
+
+            server.stop();
+        }
+
+        return Result.SUCCESS;
+    }
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/text/SortAction.java b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/text/SortAction.java
new file mode 100644
index 0000000..e81b247
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/text/SortAction.java
@@ -0,0 +1,391 @@
+/*
+ * 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.geronimo.gshell.commands.text;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.vfs.FileObject;
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.geronimo.gshell.command.CommandContext;
+import org.apache.geronimo.gshell.io.Closer;
+import org.apache.geronimo.gshell.vfs.FileObjects;
+import org.apache.geronimo.gshell.vfs.support.VfsActionSupport;
+
+/**
+ * Sort lines of text
+ *
+ * TODO: remove this file when gshell is upgraded
+ *
+ */
+public class SortAction extends VfsActionSupport {
+
+    @Option(name = "-f")
+    private boolean caseInsensitive;
+
+    @Option(name = "-r")
+    private boolean reverse;
+
+    @Option(name = "-u")
+    private boolean unique;
+
+    @Option(name = "-t")
+    private String separator;
+
+    @Option(name = "-b")
+    private boolean ignoreBlanks;
+
+    @Option(name = "-k", argumentRequired = true, multiValued = true)
+    private List<String> sortFields;
+
+    @Option(name = "-n")
+    private boolean numeric;
+
+    @Argument(index = 0, required=false)
+    private String path;
+
+
+    public Object execute(CommandContext context) throws Exception {
+        assert context != null;
+
+        if (path != null) {
+            FileObject file = resolveFile(context, path);
+
+            try {
+                sort(context, file);
+            }
+            finally {
+                FileObjects.close(file);
+            }
+        }
+        else {
+            sort(context.getIo().inputStream, context.getIo().out);
+        }
+        return Result.SUCCESS;
+    }
+
+    protected void sort(final CommandContext context, final FileObject file) throws Exception {
+        assert context != null;
+        assert file != null;
+
+        ensureFileExists(file);
+        ensureFileHasContent(file);
+        ensureFileIsReadable(file);
+
+        BufferedInputStream input = new BufferedInputStream(file.getContent().getInputStream());
+        try {
+            sort(input, context.getIo().out);
+        }
+        finally {
+            Closer.close(input);
+        }
+    }
+
+    protected void sort(InputStream input, PrintWriter out) throws Exception {
+        BufferedReader r = new BufferedReader(new InputStreamReader(input));
+        List<String> strings = new ArrayList<String>();
+        for (String s = r.readLine(); s != null; s = r.readLine()) {
+            strings.add(s);
+        }
+        char sep = (separator == null || separator.length() == 0) ? '\0' : separator.charAt(0);
+        Collections.sort(strings, new SortComparator(caseInsensitive, reverse, ignoreBlanks, numeric, sep, sortFields));
+        String last = null;
+        for (String s : strings) {
+            if (last == null) {
+                last = s;
+            } else if (!unique || !s.equals(last)) {
+                out.println(s);
+            }
+        }
+    }
+
+    public static class SortComparator implements Comparator<String> {
+
+        private boolean caseInsensitive;
+        private boolean reverse;
+        private boolean ignoreBlanks;
+        private boolean numeric;
+        private char separator;
+        private List<Key> sortKeys;
+
+        private static Pattern fpPattern;
+        static {
+            final String Digits     = "(\\p{Digit}+)";
+            final String HexDigits  = "(\\p{XDigit}+)";
+            final String Exp        = "[eE][+-]?" + Digits;
+            final String fpRegex    = "([\\x00-\\x20]*[+-]?(NaN|Infinity|(((" + Digits + "(\\.)?(" + Digits + "?)(" + Exp + ")?)|(\\.(" + Digits + ")(" + Exp + ")?)|(((0[xX]" + HexDigits + "(\\.)?)|(0[xX]" + HexDigits + "?(\\.)" + HexDigits + "))[pP][+-]?" + Digits + "))" + "[fFdD]?))[\\x00-\\x20]*)(.*)";
+            fpPattern = Pattern.compile(fpRegex);
+        }
+
+        public SortComparator(boolean caseInsensitive,
+                              boolean reverse,
+                              boolean ignoreBlanks,
+                              boolean numeric,
+                              char separator,
+                              List<String> sortFields) {
+            this.caseInsensitive = caseInsensitive;
+            this.reverse = reverse;
+            this.separator = separator;
+            this.ignoreBlanks = ignoreBlanks;
+            this.numeric = numeric;
+            if (sortFields == null || sortFields.size() == 0) {
+                sortFields = new ArrayList<String>();
+                sortFields.add("1");
+            }
+            sortKeys = new ArrayList<Key>();
+            for (String f : sortFields) {
+                sortKeys.add(new Key(f));
+            }
+        }
+
+        public int compare(String o1, String o2) {
+            int res = 0;
+
+            List<Integer> fi1 = getFieldIndexes(o1);
+            List<Integer> fi2 = getFieldIndexes(o2);
+            for (Key key : sortKeys) {
+                int[] k1 = getSortKey(o1, fi1, key);
+                int[] k2 = getSortKey(o2, fi2, key);
+                if (key.numeric) {
+                    Double d1 = getDouble(o1, k1[0], k1[1]);
+                    Double d2 = getDouble(o2, k2[0], k2[1]);
+                    res = d1.compareTo(d2);
+                } else {
+                    res = compareRegion(o1, k1[0], k1[1], o2, k2[0], k2[1], key.caseInsensitive);
+                }
+                if (res != 0) {
+                    if (key.reverse) {
+                        res = - res;
+                    }
+                    break;
+                }
+            }
+            return res;
+        }
+
+        protected Double getDouble(String s, int start, int end) {
+            Matcher m = fpPattern.matcher(s.substring(start, end));
+            m.find();
+            return new Double(s.substring(0, m.end(1)));
+        }
+
+        protected int compareRegion(String s1, int start1, int end1, String s2, int start2, int end2, boolean caseInsensitive) {
+            int n1 = end1, n2 = end2;
+            for (int i1 = start1, i2 = start2; i1 < end1 && i2 < n2; i1++, i2++) {
+                char c1 = s1.charAt(i1);
+                char c2 = s2.charAt(i2);
+                if (c1 != c2) {
+                    if (caseInsensitive) {
+                        c1 = Character.toUpperCase(c1);
+                        c2 = Character.toUpperCase(c2);
+                        if (c1 != c2) {
+                            c1 = Character.toLowerCase(c1);
+                            c2 = Character.toLowerCase(c2);
+                            if (c1 != c2) {
+                                return c1 - c2;
+                            }
+                        }
+                    } else {
+                        return c1 - c2;
+                    }
+                }
+            }
+            return n1 - n2;
+        }
+
+        protected int[] getSortKey(String str, List<Integer> fields, Key key) {
+            int start;
+            int end;
+            if (key.startField * 2 < fields.size()) {
+                start = fields.get((key.startField - 1) * 2);
+                if (key.ignoreBlanksStart) {
+                    while (start < fields.get((key.startField - 1) * 2 + 1) && Character.isWhitespace(str.charAt(start))) {
+                        start++;
+                    }
+                }
+                if (key.startChar > 0) {
+                    start = Math.min(start + key.startChar - 1, fields.get((key.startField - 1) * 2 + 1));
+                }
+            } else {
+                start = 0;
+            }
+            if (key.endField > 0 && key.endField * 2 < fields.size()) {
+                end =  fields.get((key.endField - 1) * 2);
+                if (key.ignoreBlanksEnd) {
+                    while (end < fields.get((key.endField - 1) * 2 + 1) && Character.isWhitespace(str.charAt(end))) {
+                        end++;
+                    }
+                }
+                if (key.endChar > 0) {
+                    end = Math.min(end + key.endChar - 1, fields.get((key.endField - 1) * 2 + 1));
+                }
+            } else {
+                end = str.length();
+            }
+            return new int[] { start, end };
+        }
+
+        protected List<Integer> getFieldIndexes(String o) {
+            List<Integer> fields = new ArrayList<Integer>();
+            if (o.length() > 0) {
+                if (separator == '\0') {
+                    int i = 0;
+                    fields.add(0);
+                    for (int idx = 1; idx < o.length(); idx++) {
+                        if (Character.isWhitespace(o.charAt(idx)) && !Character.isWhitespace(o.charAt(idx - 1))) {
+                            fields.add(idx - 1);
+                            fields.add(idx);
+                        }
+                    }
+                    fields.add(o.length() - 1);
+                } else {
+                    int last = -1;
+                    for (int idx = o.indexOf(separator); idx >= 0; idx = o.indexOf(separator, idx + 1)) {
+                        if (last >= 0) {
+                            fields.add(last);
+                            fields.add(idx - 1);
+                        } else if (idx > 0) {
+                            fields.add(0);
+                            fields.add(idx - 1);
+                        }
+                        last = idx + 1;
+                    }
+                    if (last < o.length()) {
+                        fields.add(last < 0 ? 0 : last);
+                        fields.add(o.length() - 1);
+                    }
+                }
+            }
+            return fields;
+        }
+
+        public class Key {
+            int startField;
+            int startChar;
+            int endField;
+            int endChar;
+            boolean ignoreBlanksStart;
+            boolean ignoreBlanksEnd;
+            boolean caseInsensitive;
+            boolean reverse;
+            boolean numeric;
+
+            public Key(String str) {
+                boolean modifiers = false;
+                boolean startPart = true;
+                boolean inField = true;
+                boolean inChar = false;
+                for (char c : str.toCharArray()) {
+                    switch (c) {
+                        case '0':
+                        case '1':
+                        case '2':
+                        case '3':
+                        case '4':
+                        case '5':
+                        case '6':
+                        case '7':
+                        case '8':
+                        case '9':
+                            if (!inField && !inChar) {
+                                throw new IllegalArgumentException("Bad field syntax: " + str);
+                            }
+                            if (startPart) {
+                                if (inChar) {
+                                    startChar = startChar * 10 + (c - '0');
+                                } else {
+                                    startField = startField * 10 + (c - '0');
+                                }
+                            } else {
+                                if (inChar) {
+                                    endChar = endChar * 10 + (c - '0');
+                                } else {
+                                    endField = endField * 10 + (c - '0');
+                                }
+                            }
+                            break;
+                        case '.':
+                            if (!inField) {
+                                throw new IllegalArgumentException("Bad field syntax: " + str);
+                            }
+                            inField = false;
+                            inChar = true;
+                            break;
+                        case 'n':
+                            inField = false;
+                            inChar = false;
+                            modifiers = true;
+                            numeric = true;
+                            break;
+                        case 'f':
+                            inField = false;
+                            inChar = false;
+                            modifiers = true;
+                            caseInsensitive = true;
+                            break;
+                        case 'r':
+                            inField = false;
+                            inChar = false;
+                            modifiers = true;
+                            reverse = true;
+                            break;
+                        case 'b':
+                            inField = false;
+                            inChar = false;
+                            modifiers = true;
+                            if (startPart) {
+                                ignoreBlanksStart = true;
+                            } else {
+                                ignoreBlanksEnd = true;
+                            }
+                            break;
+                        case ',':
+                            inField = true;
+                            inChar = false;
+                            startPart = false;
+                            break;
+                        default:
+                            throw new IllegalArgumentException("Bad field syntax: " + str);
+                    }
+                }
+                if (!modifiers) {
+                    ignoreBlanksStart = ignoreBlanksEnd = SortComparator.this.ignoreBlanks;
+                    reverse = SortComparator.this.reverse;
+                    caseInsensitive = SortComparator.this.caseInsensitive;
+                    numeric = SortComparator.this.numeric;
+                }
+                if (startField < 1) {
+                    throw new IllegalArgumentException("Bad field syntax: " + str);
+                }
+            }
+        }
+    }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/vfs/provider/meta/MetaFileObject.java b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/vfs/provider/meta/MetaFileObject.java
new file mode 100644
index 0000000..7e71328
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/vfs/provider/meta/MetaFileObject.java
@@ -0,0 +1,144 @@
+/*
+ * 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.geronimo.gshell.vfs.provider.meta;
+
+import org.apache.commons.vfs.FileContentInfoFactory;
+import org.apache.commons.vfs.FileName;
+import org.apache.commons.vfs.FileType;
+import org.apache.commons.vfs.FileContent;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.provider.AbstractFileObject;
+import org.apache.commons.vfs.provider.DefaultFileContent;
+import org.apache.geronimo.gshell.vfs.provider.meta.data.MetaData;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.Map;
+
+/**
+ * Meta file object.
+ *
+ * TODO: remove this file when gshell version is upgraded
+ *
+ * @version $Rev: 706033 $ $Date: 2008-10-19 17:36:15 +0200 (Sun, 19 Oct 2008) $
+ */
+public class MetaFileObject
+    extends AbstractFileObject
+{
+    private final MetaFileSystem fileSystem;
+
+    private MetaData data;
+    private FileContent content;
+
+    public MetaFileObject(final FileName fileName, final MetaFileSystem fileSystem) {
+        super(fileName, fileSystem);
+
+        // Save for uncasted typed access
+        this.fileSystem = fileSystem;
+    }
+
+    public MetaData getData() {
+        if (data == null) {
+            throw new IllegalStateException("Meta data has not been attached");
+        }
+
+        return data;
+    }
+
+    @Override
+    protected FileType doGetType() throws Exception {
+        return getData().getType();
+    }
+
+    @Override
+    protected long doGetLastModifiedTime() throws Exception {
+        return getData().getLastModified();
+    }
+
+    @Override
+    protected boolean doIsReadable() throws Exception {
+        return data.getBuffer() != null;
+    }
+
+    @Override
+    protected boolean doIsWriteable() throws Exception {
+        return false;
+    }
+
+    @Override
+    protected FileContentInfoFactory getFileContentInfoFactory() {
+        return fileSystem.getFileContentInfoFactory();
+    }
+
+    @Override
+    protected long doGetContentSize() throws Exception {
+        byte[] bytes = data.getBuffer();
+        return bytes != null ? bytes.length : 0;
+    }
+
+    @Override
+    protected InputStream doGetInputStream() throws Exception {
+        byte[] bytes = data.getBuffer();
+        if (bytes != null) {
+            return new ByteArrayInputStream(bytes);
+        }
+
+        return null;
+    }
+
+    @Override
+    protected Map<String,Object> doGetAttributes() {
+        return getData().getAttributes();
+    }
+
+    @Override
+    protected void doSetAttribute(final String name, final Object value) {
+        getData().getAttributes().put(name, value);
+    }
+
+    protected void doRemoveAttribute(final String name) {
+        getData().getAttributes().remove(name);
+    }
+
+    @Override
+    protected String[] doListChildren() throws Exception {
+        return fileSystem.listChildren(getName());
+    }
+
+    @Override
+    protected void doAttach() throws Exception {
+        if (data == null) {
+            data = fileSystem.lookupData(this);
+        }
+        content = new DefaultFileContent(this, getFileContentInfoFactory());
+    }
+
+    @Override
+    protected void doDetach() throws Exception {
+        data = null;
+        content = null;
+    }
+
+    @Override
+    public FileContent getContent() throws FileSystemException {
+        super.getContent();
+        return content;
+    }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ApplicationImpl.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ApplicationImpl.java
new file mode 100644
index 0000000..350fd63
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ApplicationImpl.java
@@ -0,0 +1,143 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.URL;
+import java.util.Properties;
+
+import org.apache.geronimo.gshell.application.Application;
+import org.apache.geronimo.gshell.application.ClassPath;
+import org.apache.geronimo.gshell.application.model.ApplicationModel;
+import org.apache.geronimo.gshell.artifact.Artifact;
+import org.apache.geronimo.gshell.command.Variables;
+import org.apache.geronimo.gshell.io.IO;
+import org.springframework.beans.factory.InitializingBean; 
+
+public class ApplicationImpl implements Application, InitializingBean  {
+
+	private static final String EMBEDDED_PROPS = "org/apache/servicemix/kernel/version/embedded.properties";
+    private static final String SERVICEMIX_VERSION ="org/apache/servicemix/kernel/gshell/core/servicemix-version.properties";
+    private static final String VERSION_PROPERTY = "version";
+
+    private String id;
+    private IO io;
+    private ApplicationModel model;
+    private Variables variables;
+    private InetAddress localHost;
+    private File homeDir;         
+    private URL embeddedResource = null; 
+
+    public ApplicationImpl() throws Exception {
+        this.localHost = InetAddress.getLocalHost();
+        this.homeDir = detectHomeDir();    
+    }           
+     
+    public void afterPropertiesSet() throws Exception {      	         	    	
+        Properties props = new Properties();
+        props.load(getClass().getClassLoader().getResourceAsStream(SERVICEMIX_VERSION));
+        String kernelVersion = props.getProperty(VERSION_PROPERTY);
+        this.model.setVersion(kernelVersion);
+    	ServiceMixBranding smxBranding = (ServiceMixBranding) this.model.getBranding();
+        smxBranding.setVersion(kernelVersion);
+        
+     	if (this.getClass().getClassLoader().getResource(EMBEDDED_PROPS) != null) {                    
+            embeddedResource = this.getClass().getClassLoader().getResource(EMBEDDED_PROPS);
+            smxBranding.setEmbeddedResource(embeddedResource);
+        }
+    }
+    
+    public URL getEmbeddedResource() {
+    	return embeddedResource;
+    }        
+        
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public IO getIo() {
+        return io;
+    }
+
+    public void setIo(IO io) {
+        this.io = io;
+    }
+
+    public Variables getVariables() {
+        return variables;
+    }
+
+    public void setVariables(Variables variables) {
+        this.variables = variables;
+    }
+
+    public ApplicationModel getModel() {
+        return model;
+    }
+
+    public void setModel(ApplicationModel model) {
+        this.model = model;
+    }
+
+    public ClassPath getClassPath() {
+        throw new UnsupportedOperationException();
+    }
+
+    public File getHomeDir() {
+        if (homeDir == null) {
+            throw new IllegalStateException();
+        }
+        return homeDir;
+    }
+
+    public InetAddress getLocalHost() {
+        return localHost;
+    }
+
+    public String getUserName() {
+        return System.getProperty("user.name");
+    }
+
+    public Artifact getArtifact() {
+        return null;
+    }
+
+    private File detectHomeDir() {
+        String homePath = System.getProperty("user.home");
+        // And now lets resolve this sucker
+        File dir;
+        try {
+            dir = new File(homePath).getCanonicalFile();
+        }
+        catch (IOException e) {
+            throw new RuntimeException("Failed to resolve home directory: " + homePath, e);
+        }
+        // And some basic sanity too
+        if (!dir.exists() || !dir.isDirectory()) {
+            throw new RuntimeException("Home directory configured but is not a valid directory: " + dir);
+        }
+
+        return dir;
+    }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ApplicationManagerImpl.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ApplicationManagerImpl.java
new file mode 100644
index 0000000..7465900
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ApplicationManagerImpl.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.core;
+
+import org.apache.geronimo.gshell.application.Application;
+import org.apache.geronimo.gshell.application.ApplicationConfiguration;
+import org.apache.geronimo.gshell.application.ApplicationManager;
+import org.apache.geronimo.gshell.event.EventPublisher;
+import org.apache.geronimo.gshell.io.SystemOutputHijacker;
+import org.apache.geronimo.gshell.shell.Shell;
+import org.apache.geronimo.gshell.wisdom.application.ShellCreatedEvent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+public class ApplicationManagerImpl implements ApplicationManager, ApplicationContextAware, InitializingBean, DisposableBean {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private EventPublisher eventPublisher;
+
+    private Application application;
+
+    private ApplicationContext applicationContext;
+
+    public ApplicationManagerImpl(EventPublisher eventPublisher, Application application) {
+        this.eventPublisher = eventPublisher;
+        this.application = application;
+    }
+
+    public void afterPropertiesSet() throws Exception {
+        if (!SystemOutputHijacker.isInstalled()) {
+            SystemOutputHijacker.install();
+        }
+        //SystemOutputHijacker.register(application.getIo().outputStream, application.getIo().errorStream);
+    }
+
+    public void destroy() {
+        SystemOutputHijacker.uninstall();
+    }
+
+    public void setApplicationContext(ApplicationContext applicationContext) {
+        this.applicationContext = applicationContext;
+    }
+
+    public void configure(ApplicationConfiguration applicationConfiguration) throws Exception {
+        throw new UnsupportedOperationException();
+    }
+
+    public Application getApplication() {
+        return application;
+    }
+
+    public Shell create() throws Exception {
+        final Shell shell = (Shell) applicationContext.getBean("shell");
+
+        log.debug("Created shell instance: {}", shell);
+
+        eventPublisher.publish(new ShellCreatedEvent(shell));
+
+        return shell;
+    }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/BeanContainerAwareProcessor.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/BeanContainerAwareProcessor.java
new file mode 100644
index 0000000..7deacb1
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/BeanContainerAwareProcessor.java
@@ -0,0 +1,55 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import org.apache.geronimo.gshell.spring.BeanContainer;
+import org.apache.geronimo.gshell.spring.BeanContainerAware;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+public class BeanContainerAwareProcessor implements InitializingBean, BeanPostProcessor, ApplicationContextAware {
+
+    private ApplicationContext applicationContext;
+    private BeanContainer container;
+
+    public void setApplicationContext(ApplicationContext applicationContext) {
+        this.applicationContext = applicationContext;
+    }
+
+    public void afterPropertiesSet() throws Exception {
+        this.container = new BeanContainerWrapper(applicationContext);
+    }
+
+    public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
+        assert bean != null;
+
+        if (bean instanceof BeanContainerAware) {
+            ((BeanContainerAware)bean).setBeanContainer(container);
+        }
+
+        return bean;
+    }
+
+    public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
+        return bean;
+    }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/BeanContainerWrapper.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/BeanContainerWrapper.java
new file mode 100644
index 0000000..edc15d7
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/BeanContainerWrapper.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.servicemix.kernel.gshell.core;
+
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.geronimo.gshell.spring.BeanContainer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.context.ApplicationContext;
+
+public class BeanContainerWrapper implements BeanContainer {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private ApplicationContext context;
+
+    public BeanContainerWrapper(ApplicationContext context) {
+        this.context = context;
+    }
+
+    public BeanContainer getParent() {
+        return null;
+    }
+
+    public ClassLoader getClassLoader() {
+        return context.getClassLoader();
+    }
+
+    public void loadBeans(String[] strings) throws Exception {
+        throw new UnsupportedOperationException();
+    }
+
+    public <T> T getBean(Class<T> type) {
+        assert type != null;
+
+        log.trace("Getting bean of type: {}", type);
+
+        String[] names = context.getBeanNamesForType(type);
+
+        if (names.length == 0) {
+            throw new NoSuchBeanDefinitionException(type, "No bean defined for type: " + type);
+        }
+        if (names.length > 1) {
+            throw new NoSuchBeanDefinitionException(type, "No unique bean defined for type: " + type + ", found matches: " + Arrays.asList(names));
+        }
+
+        return getBean(names[0], type);
+    }
+
+    public <T> T getBean(String name, Class<T> requiredType) {
+        assert name != null;
+        assert requiredType != null;
+
+        log.trace("Getting bean named '{}' of type: {}", name, requiredType);
+
+        return (T) context.getBean(name, requiredType);
+    }
+
+    public <T> Map<String, T> getBeans(Class<T> type) {
+        assert type != null;
+
+        log.trace("Getting beans of type: {}", type);
+
+        return (Map<String,T>) context.getBeansOfType(type);
+    }
+
+    public String[] getBeanNames() {
+        log.trace("Getting bean names");
+
+        return context.getBeanDefinitionNames();
+    }
+
+    public String[] getBeanNames(Class type) {
+        assert type != null;
+
+        log.trace("Getting bean names of type: {}", type);
+
+        return context.getBeanNamesForType(type);
+    }
+
+    public BeanContainer createChild(Collection<URL> urls) {
+        throw new UnsupportedOperationException();
+    }
+
+    public BeanContainer createChild() {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/CommandBundle.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/CommandBundle.java
new file mode 100644
index 0000000..2111072
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/CommandBundle.java
@@ -0,0 +1,175 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.geronimo.gshell.command.Alias;
+import org.apache.geronimo.gshell.command.Command;
+import org.apache.geronimo.gshell.command.Link;
+import org.apache.geronimo.gshell.registry.AliasRegistry;
+import org.apache.geronimo.gshell.registry.CommandRegistry;
+import org.apache.geronimo.gshell.wisdom.command.LinkCommand;
+import org.apache.geronimo.gshell.wisdom.registry.CommandLocationImpl;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.osgi.context.BundleContextAware;
+
+public class CommandBundle implements BundleContextAware, InitializingBean, DisposableBean, ApplicationContextAware {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private CommandRegistry commandRegistry;
+
+    private AliasRegistry aliasRegistry;
+
+    private BundleContext bundleContext;
+
+    private List<Command> commands;
+
+    private List<Link> links;
+
+    private List<Alias> aliases;
+
+    private ApplicationContext applicationContext;
+
+    private List<ServiceRegistration> registrations = new ArrayList<ServiceRegistration>();
+
+    public CommandBundle() {
+    }
+
+    public void setApplicationContext(ApplicationContext applicationContext) {
+        this.applicationContext = applicationContext;
+    }
+
+    public List<Command> getCommands() {
+        return commands;
+    }
+
+    public void setCommands(final List<Command> commands) {
+        assert commands != null;
+
+        this.commands = commands;
+    }
+
+    public List<Link> getLinks() {
+        return links;
+    }
+
+    public void setLinks(List<Link> links) {
+        assert links != null;
+
+        this.links = links;
+    }
+
+    public List<Alias> getAliases() {
+        return aliases;
+    }
+
+    public void setAliases(List<Alias> aliases) {
+        assert aliases != null;
+
+        this.aliases = aliases;
+    }
+
+    public void setBundleContext(BundleContext bundleContext) {
+        this.bundleContext = bundleContext;
+    }
+
+    public void afterPropertiesSet() throws Exception {
+        log.debug("Initializing command bundle");
+        if (commandRegistry == null) {
+            String[] names = applicationContext.getBeanNamesForType(CommandRegistry.class);
+            if (names.length == 1) {
+                commandRegistry = (CommandRegistry) applicationContext.getBean(names[0], CommandRegistry.class);
+            }
+        }
+        if (aliasRegistry == null) {
+            String[] names = applicationContext.getBeanNamesForType(AliasRegistry.class);
+            if (names.length == 1) {
+                aliasRegistry = (AliasRegistry) applicationContext.getBean(names[0], AliasRegistry.class);
+            }
+        }
+        if (commandRegistry != null && aliasRegistry != null) {
+            log.debug("Command bundle is using the auto wired command/alias registry");
+            if (commands != null) {
+                for (Command command : commands) {
+                    log.debug("Registering command: {}", command.getLocation());
+                    commandRegistry.registerCommand(command);
+                }
+            }
+            if (links != null) {
+                for (Link link : links) {
+                    log.debug("Registering link: {}", link.getName());
+                    LinkCommand cmd = new LinkCommand(commandRegistry, link.getTarget());
+                    cmd.setLocation(new CommandLocationImpl(link.getName()));
+                    commandRegistry.registerCommand(cmd);
+                }
+            }
+            if (aliases != null) {
+                for (Alias alias : aliases) {
+                    log.debug("Registering alias: {}", alias.getName());
+                    aliasRegistry.registerAlias(alias.getName(), alias.getAlias());
+                }
+            }
+        } else if (bundleContext != null) {
+            log.debug("Command bundle is using the OSGi registry");
+            if (commands != null) {
+                for (Command command : commands) {
+                    log.debug("Registering command: {}", command.getLocation());
+                    Dictionary props = new Properties();
+                    props.put(OsgiCommandRegistry.NAME, command.getLocation().getFullPath());
+                    registrations.add(bundleContext.registerService(Command.class.getName(), command, props));
+                }
+            }
+            if (links != null) {
+                for (Link link : links) {
+                    log.debug("Registering link: {}", link.getName());
+                    registrations.add(bundleContext.registerService(Link.class.getName(), link, new Properties()));
+                }
+            }
+            if (aliases != null) {
+                for (Alias alias : aliases) {
+                    log.debug("Registering alias: {}", alias.getName());
+                    Dictionary props = new Properties();
+                    registrations.add(bundleContext.registerService(Alias.class.getName(), alias, new Properties()));
+                }
+            }
+        } else {
+            throw new Exception("Command bundle should be wired to the command/alias registry or be used in an OSGi context");
+        }
+    }
+
+    public void destroy() {
+        log.debug("Destroying command bundle");
+        for (ServiceRegistration reg : registrations) {
+            reg.unregister();
+        }
+    }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/LocalConsole.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/LocalConsole.java
new file mode 100644
index 0000000..98c7193
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/LocalConsole.java
@@ -0,0 +1,172 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.geronimo.gshell.notification.ExitNotification;
+import org.apache.geronimo.gshell.shell.Shell;
+import org.apache.servicemix.kernel.main.spi.MainService;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.osgi.context.BundleContextAware;
+
+public class LocalConsole implements Runnable, BundleContextAware {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private Shell shell;
+
+    private boolean createLocalShell;
+
+    private BundleContext bundleContext;
+
+    private MainService mainService;
+
+    private CountDownLatch frameworkStarted;
+    
+    public BundleContext getBundleContext() {
+        return bundleContext;
+    }
+
+    public void setBundleContext(BundleContext bundleContext) {
+        this.bundleContext = bundleContext;
+    }
+
+    public MainService getMainService() {
+        return mainService;
+    }
+
+    public void setMainService(MainService mainService) {
+        this.mainService = mainService;
+    }
+
+    public Shell getShell() {
+        return shell;
+    }
+
+    public void setShell(Shell shell) {
+        this.shell = shell;
+    }
+
+    public boolean isCreateLocalShell() {
+        return createLocalShell;
+    }
+
+    public void setCreateLocalShell(boolean createLocalShell) {
+        this.createLocalShell = createLocalShell;
+    }
+
+    public void init() {
+        shell.getContext().getVariables().set("gshell.username", "smx");
+        frameworkStarted = new CountDownLatch(1);
+		getBundleContext().addFrameworkListener(new FrameworkListener(){
+			public void frameworkEvent(FrameworkEvent event) {
+				log.debug("Got event: " + event.getType());
+				if( event.getType() == FrameworkEvent.STARTED ) {
+					frameworkStarted.countDown();
+				}
+			}
+		});
+        if (createLocalShell) {
+            new Thread(this, "localShell").start();
+        }
+    }
+
+    public void destroy() {
+        if (createLocalShell) {
+            shell.close();
+        }
+    }
+
+    public void run() {
+        try {
+            String[] args = mainService.getArgs();
+            // If a command was specified on the command line, then just execute that command.
+            if (args != null && args.length > 0) {
+                waitForFrameworkToStart();
+                log.info("Executing Shell with arguments: " + Arrays.toString(args));
+                StringBuilder sb = new StringBuilder();
+                for (String arg : args) {
+                    sb.append(arg).append(" ");
+                }
+                Object value = shell.execute(sb.toString());
+                if (mainService != null) {
+                    if (value instanceof Number) {
+                        mainService.setExitCode(((Number) value).intValue());
+                    } else {
+                        mainService.setExitCode(value != null ? 1 : 0);
+                    }
+                    log.info("Exiting shell due to terminated command");
+                }
+            } else {
+                shell.run();
+            }
+        } catch (ExitNotification e) {
+            if (mainService != null) {
+                mainService.setExitCode(0);
+            }
+            log.info("Exiting shell due received exit notification");
+        } catch (Throwable e) {
+            if (mainService != null) {
+                mainService.setExitCode(-1);
+            }
+            log.error("Exiting shell due to caught exception " + e, e);
+        } finally {
+            try {
+                shell.close();
+            } catch (Throwable t) {}
+            asyncShutdown();
+        }
+    }
+
+    /**
+     * Blocks until the framework has finished starting.  We do this so that any installed
+     * bundles for commands get fully registered.
+     *
+     * @throws InterruptedException
+     */
+    private void waitForFrameworkToStart() throws InterruptedException {
+        log.info("Waiting from framework to start.");
+        if (frameworkStarted.await(60, TimeUnit.SECONDS)) {
+			log.info("System completed startup.");
+		} else {
+			log.warn("System took too long startup... continuing");
+		}
+    }
+
+    private void asyncShutdown() {
+        new Thread() {
+            public void run() {
+                try {
+                    getBundleContext().getBundle(0).stop();
+                } catch (BundleException e) {
+                    log.info("Caught exception while shutting down framework: " + e, e);
+                }
+            }
+        }.start();
+    }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiAliasRegistry.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiAliasRegistry.java
new file mode 100644
index 0000000..a09ce0b
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiAliasRegistry.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.servicemix.kernel.gshell.core;
+
+import java.util.Map;
+
+import org.apache.geronimo.gshell.command.Alias;
+import org.apache.geronimo.gshell.registry.AliasRegistry;
+
+public class OsgiAliasRegistry {
+
+    public static final String NAME = "name";
+    public static final String ALIAS = "alias";
+
+    private AliasRegistry aliasRegistry;
+
+    public OsgiAliasRegistry(AliasRegistry aliasRegistry) {
+        this.aliasRegistry = aliasRegistry;
+    }
+
+    public void register(final Alias alias, Map<String, ?> properties) throws Exception {
+        aliasRegistry.registerAlias(alias.getName(), alias.getAlias());
+    }
+
+    public void unregister(final Alias alias, Map<String, ?> properties) throws Exception {
+        aliasRegistry.removeAlias(alias.getName());
+    }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiCommandRegistry.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiCommandRegistry.java
new file mode 100644
index 0000000..c6817e8
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiCommandRegistry.java
@@ -0,0 +1,58 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import java.util.Map;
+
+import org.apache.geronimo.gshell.command.Command;
+import org.apache.geronimo.gshell.command.Link;
+import org.apache.geronimo.gshell.registry.CommandRegistry;
+import org.apache.geronimo.gshell.wisdom.command.LinkCommand;
+import org.apache.geronimo.gshell.wisdom.registry.CommandLocationImpl;
+
+public class OsgiCommandRegistry {
+
+    public static final String NAME = "name";
+    public static final String TARGET = "target";
+
+    private CommandRegistry commandRegistry;
+
+    public OsgiCommandRegistry(CommandRegistry commandRegistry) {
+        this.commandRegistry = commandRegistry;
+    }
+
+    public void register(final Command command, Map<String, ?> properties) throws Exception {
+        commandRegistry.registerCommand(command);
+    }
+
+    public void unregister(final Command command, Map<String, ?> properties) throws Exception {
+        commandRegistry.removeCommand(command);
+    }
+
+    public void register(final Link link, Map<String, ?> properties) throws Exception {
+        LinkCommand cmd = new LinkCommand(commandRegistry, link.getTarget());
+        cmd.setLocation(new CommandLocationImpl(link.getName()));
+        commandRegistry.registerCommand(cmd);
+    }
+
+    public void unregister(final Link link, Map<String, ?> properties) throws Exception {
+        commandRegistry.removeCommand(commandRegistry.getCommand(link.getName()));
+    }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiCommandSupport.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiCommandSupport.java
new file mode 100644
index 0000000..e13940a
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiCommandSupport.java
@@ -0,0 +1,96 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.geronimo.gshell.command.CommandAction;
+import org.apache.geronimo.gshell.command.CommandContext;
+import org.apache.geronimo.gshell.command.Variables;
+import org.apache.geronimo.gshell.io.IO;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.springframework.osgi.context.BundleContextAware;
+
+public abstract class OsgiCommandSupport implements CommandAction, BundleContextAware {
+
+    protected Log log = LogFactory.getLog(getClass());
+    protected BundleContext bundleContext;
+    protected CommandContext commandContext;
+    protected IO io;
+    protected Variables variables;
+    protected List<ServiceReference> usedReferences;
+    
+    public Object execute(CommandContext commandContext) throws Exception {
+        this.commandContext = commandContext;
+        this.io = commandContext.getIo();
+        this.variables = commandContext.getVariables();
+        try {
+            return doExecute();
+        } finally {
+            ungetServices();
+        }
+    }
+
+    protected abstract Object doExecute() throws Exception;
+
+    public BundleContext getBundleContext() {
+        return bundleContext;
+    }
+
+    public void setBundleContext(BundleContext bundleContext) {
+        this.bundleContext = bundleContext;
+    }
+
+    protected <T> List<T> getAllServices(Class<T> clazz, String filter) throws Exception {
+        ServiceReference[] references = getBundleContext().getAllServiceReferences(clazz.getName(), filter);
+        if (references == null) {
+            return null;
+        }
+        List<T> services = new ArrayList<T>();
+        for (ServiceReference ref : references) {
+            T t = getService(clazz, ref);
+            services.add(t);
+        }
+        return services;
+    }
+
+    protected <T> T getService(Class<T> clazz, ServiceReference reference) {
+        T t = (T) getBundleContext().getService(reference);
+        if (t != null) {
+            if (usedReferences == null) {
+                usedReferences = new ArrayList<ServiceReference>();
+            }
+            usedReferences.add(reference);
+        }
+        return t;
+    }
+
+    protected void ungetServices() {
+        if (usedReferences != null) {
+            for (ServiceReference ref : usedReferences) {
+                getBundleContext().ungetService(ref);
+            }
+        }
+    }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ServiceMixBranding.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ServiceMixBranding.java
new file mode 100644
index 0000000..803b511
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ServiceMixBranding.java
@@ -0,0 +1,188 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URL;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import org.apache.geronimo.gshell.ansi.AnsiBuffer;
+import org.apache.geronimo.gshell.ansi.AnsiCode;
+import org.apache.geronimo.gshell.ansi.AnsiRenderWriter;
+import org.apache.geronimo.gshell.application.model.Branding;
+
+public class ServiceMixBranding extends Branding {		
+   
+    private String prompt; 
+    private String[] banner;
+    private String displayName;
+    private String displayVersion;    
+    private String displayLocation;
+    private String applicationName;
+    private String applicationVersion;
+    private String applicationLocation;
+    
+    private String[] kernelBanner = {
+        " ____                  _          __  __ _      ",
+        "/ ___|  ___ _ ____   _(_) ___ ___|  \\/  (_)_  __",
+        "\\___ \\ / _ \\ '__\\ \\ / / |/ __/ _ \\ |\\/| | \\ \\/ /",
+        " ___) |  __/ |   \\ V /| | (_|  __/ |  | | |>  < ",
+        "|____/ \\___|_|    \\_/ |_|\\___\\___|_|  |_|_/_/\\_\\",
+    };
+
+    public ServiceMixBranding() {
+    	banner = kernelBanner;
+    	displayName = "ServiceMix Kernel";
+    	displayLocation = "http://servicemix.apache.org/kernel/";    	
+    }
+    
+    public void setEmbeddedResource(URL embeddedResource) {    	
+    	Properties brandProps = loadPropertiesFile(embeddedResource);        
+    	String brandBanner = brandProps.getProperty("banner");
+    	int i = 0;
+        char quot = '"';
+
+        StringTokenizer st = new StringTokenizer(brandBanner, ",");
+        banner = new String[st.countTokens()];
+
+        while (st.hasMoreTokens()) {        	
+            banner[i] = st.nextToken();
+            banner[i] = banner[i].substring(1, banner[i].lastIndexOf(quot));        
+            i++;
+        }
+
+    	applicationName = brandProps.getProperty("application.name");  
+    	applicationVersion = brandProps.getProperty("application.version");
+    	applicationLocation = brandProps.getProperty("application.location");
+    }    
+    
+    public String getName() {
+        return "servicemix";
+    }
+        
+    public String getDisplayName() {    	
+        return displayName;
+    }
+        
+    public void setVersion(String version) {
+    	displayVersion = version;
+    }
+    
+    public String getVersion() {
+    	return displayVersion;
+    }        
+    
+    public String getApplicationName() {
+    	return applicationName;
+    }
+
+    public String getApplicationVersion() {
+    	return applicationVersion;
+    }
+    
+    public String getProgramName() {
+        throw new UnsupportedOperationException();
+    }
+
+    public String getPrompt() {
+        return prompt;
+    }
+
+    public void setPrompt(String prompt) {
+        this.prompt = prompt;
+    }
+
+    public String getAboutMessage() {
+        StringWriter writer = new StringWriter();
+        PrintWriter out = new AnsiRenderWriter(writer);
+
+        out.println("For information about @|cyan " + displayName + "|, visit:");
+        out.println("    @|bold " + displayLocation + "| ");
+        out.flush();
+
+        if (applicationName != null && applicationVersion != null) {
+        	out.println();
+        	out.println(applicationName + " " + applicationVersion);
+        	out.println();
+        	if (applicationLocation != null) {
+                out.println("For information about @|cyan " + applicationName + "|, visit:");
+                out.println("    @|bold " + applicationLocation + "| ");
+                out.flush();
+        	}
+        }
+
+        return writer.toString();
+    }      
+    
+    public String getWelcomeMessage() {    	        
+        StringWriter writer = new StringWriter();
+        PrintWriter out = new AnsiRenderWriter(writer);
+    	
+        AnsiBuffer buff = new AnsiBuffer();        
+                
+        for (String line : banner) {
+            buff.attrib(line, AnsiCode.CYAN);
+            out.println(buff);
+        }
+
+        out.println();
+        out.println(" @|bold " + displayName + "| (" + displayVersion + ")");
+        if (applicationName != null && applicationVersion != null) {
+            out.println(" @|bold " + applicationName + "| (" + applicationVersion + ")");
+        }
+        out.println();
+        out.println("Type '@|bold help|' for more information.");
+        out.flush();
+
+        return writer.toString();
+    }
+            
+    private static Properties loadPropertiesFile(URL brandPropURL) {
+        // Read the properties file.
+        Properties brandProps = new Properties();
+        InputStream is = null;
+        try {
+            is = brandPropURL.openConnection().getInputStream();
+            brandProps.load(is);
+            is.close();
+        }
+        catch (FileNotFoundException ex) {
+            // Ignore file not found.
+        }
+        catch (Exception ex) {
+            System.err.println(
+                    "Error loading embedded properties from " + brandPropURL);
+            System.err.println("ServicemixBranding: " + ex);
+            try {
+                if (is != null) is.close();
+            }
+            catch (IOException ex2) {
+                // Nothing we can do.
+            }
+            return null;
+        }
+       
+        return brandProps;
+    }
+    
+}
+
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ShellWrapper.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ShellWrapper.java
new file mode 100644
index 0000000..ae86534
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ShellWrapper.java
@@ -0,0 +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.
+ */
+package org.apache.servicemix.kernel.gshell.core;
+
+import org.apache.geronimo.gshell.shell.Shell;
+import org.apache.geronimo.gshell.shell.ShellContext;
+import org.apache.geronimo.gshell.shell.ShellContextHolder;
+
+public class ShellWrapper implements Shell {
+
+    private Shell delegate;
+
+    public ShellWrapper(Shell delegate) {
+        this.delegate = delegate;
+    }
+
+    public ShellContext getContext() {
+        return delegate.getContext();
+    }
+
+    public Object execute(String s) throws Exception {
+        ShellContext ctx = ShellContextHolder.get(true);
+        try {
+            ShellContextHolder.set(getContext());
+            return delegate.execute(s);
+        } finally {
+            ShellContextHolder.set(ctx);
+        }
+    }
+
+    public Object execute(String s, Object[] objects) throws Exception {
+        ShellContext ctx = ShellContextHolder.get(true);
+        try {
+            ShellContextHolder.set(getContext());
+            return delegate.execute(s, objects);
+        } finally {
+            ShellContextHolder.set(ctx);
+        }
+    }
+
+    public Object execute(Object... objects) throws Exception {
+        ShellContext ctx = ShellContextHolder.get(true);
+        try {
+            ShellContextHolder.set(getContext());
+            return delegate.execute(objects);
+        } finally {
+            ShellContextHolder.set(ctx);
+        }
+    }
+
+    public boolean isOpened() {
+        return delegate.isOpened();
+    }
+
+    public void close() {
+        delegate.close();
+    }
+
+    public boolean isInteractive() {
+        return delegate.isInteractive();
+    }
+
+    public void run(Object... objects) throws Exception {
+        ShellContext ctx = ShellContextHolder.get(true);
+        try {
+            ShellContextHolder.set(getContext());
+            delegate.run(objects);
+        } finally {
+            ShellContextHolder.set(ctx);
+        }
+    }
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/WorkAroundAliasCommand.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/WorkAroundAliasCommand.java
new file mode 100644
index 0000000..1e52177
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/WorkAroundAliasCommand.java
@@ -0,0 +1,118 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.command.CommandAction;
+import org.apache.geronimo.gshell.command.CommandContext;
+import org.apache.geronimo.gshell.command.CommandResult;
+import org.apache.geronimo.gshell.command.Variables;
+import org.apache.geronimo.gshell.commandline.CommandLineExecutor;
+import org.apache.geronimo.gshell.io.IO;
+import org.apache.geronimo.gshell.shell.Shell;
+import org.apache.geronimo.gshell.shell.ShellContext;
+import org.apache.geronimo.gshell.shell.ShellContextHolder;
+import org.apache.geronimo.gshell.wisdom.command.AliasCommand;
+
+public class WorkAroundAliasCommand extends AliasCommand {
+
+    private final CommandLineExecutor executor;
+
+    public WorkAroundAliasCommand(CommandLineExecutor executor) {
+        super(executor);
+        this.executor = executor;
+        setAction(new AliasCommandAction());
+    }
+
+    @Override
+    protected void prepareAction(final ShellContext context, final Object[] args) {
+        // HACK: Reset state for proper appendArgs muck
+        assert context != null;
+        assert args != null;
+
+        setAction(new AliasCommandAction());
+        log.trace("Preparing action");
+
+        IO io = context.getIo();
+        CommandAction action = getAction();
+
+        // Setup the command action
+        try {
+            // Process command line options/arguments
+            processArguments(io, action, args);
+        }
+        catch (Exception e) {
+            // Abort if preparation caused a failure
+            throw new AbortExecutionNotification(new CommandResult.FailureResult(e));
+        }
+    }
+
+    private class AliasCommandAction
+        implements CommandAction
+    {
+        @Argument
+        private List<String> appendArgs = null;
+
+        public Object execute(final CommandContext context) throws Exception {
+            assert context != null;
+
+            StringBuilder buff = new StringBuilder();
+            buff.append(getAlias());
+
+            // If we have args to append, then do it
+            if (appendArgs != null && !appendArgs.isEmpty()) {
+                buff.append(" ");
+
+                // Append args quoted as they have already been processed by the parser
+                Iterator iter = appendArgs.iterator();
+                while (iter.hasNext()) {
+                    //
+                    // HACK: Using double quote instead of single quote for now as the parser's handling of single quote is broken
+                    //
+
+                    buff.append('"').append(iter.next()).append('"');
+                    if (iter.hasNext()) {
+                        buff.append(" ");
+                    }
+                }
+            }
+
+            log.debug("Executing alias: {}", buff);
+
+            final Shell shell = ShellContextHolder.get().getShell();
+            ShellContext shellContext = new ShellContext() {
+                public Shell getShell() {
+                    return shell;
+                }
+                public IO getIo() {
+                    return context.getIo();
+                }
+                public Variables getVariables() {
+                    return context.getVariables();
+                }
+            };
+            Object result = executor.execute(shellContext, buff.toString());
+
+            log.debug("Alias result: {}", result);
+
+            return result;
+        }
+    }
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/commands/InfoAction.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/commands/InfoAction.java
new file mode 100644
index 0000000..8f3b580
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/commands/InfoAction.java
@@ -0,0 +1,177 @@
+/*
+ * 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.servicemix.kernel.gshell.core.commands;
+
+import java.lang.management.ClassLoadingMXBean;
+import java.lang.management.GarbageCollectorMXBean;
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryMXBean;
+import java.lang.management.OperatingSystemMXBean;
+import java.lang.management.RuntimeMXBean;
+import java.lang.management.ThreadMXBean;
+import java.lang.reflect.Method;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.NumberFormat;
+import java.util.Locale;
+
+import org.apache.geronimo.gshell.ansi.AnsiCode;
+import org.apache.geronimo.gshell.ansi.AnsiRenderer;
+import org.apache.geronimo.gshell.command.CommandAction;
+import org.apache.geronimo.gshell.command.CommandContext;
+import org.apache.geronimo.gshell.io.IO;
+import org.apache.servicemix.kernel.gshell.core.ServiceMixBranding;
+import org.codehaus.plexus.util.StringUtils;
+
+public class InfoAction implements CommandAction {
+
+    private ServiceMixBranding branding;
+
+    private IO io;
+
+    private AnsiRenderer renderer = new AnsiRenderer();
+    private NumberFormat fmtI = new DecimalFormat("###,###", new DecimalFormatSymbols(Locale.ENGLISH));
+    private NumberFormat fmtD = new DecimalFormat("###,##0.000", new DecimalFormatSymbols(Locale.ENGLISH));
+
+    public InfoAction(ServiceMixBranding branding) {
+        this.branding = branding;
+    }
+
+    public Object execute(CommandContext context) throws Exception {
+        int maxNameLen;
+
+        RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
+        OperatingSystemMXBean os = ManagementFactory.getOperatingSystemMXBean();
+        ThreadMXBean threads = ManagementFactory.getThreadMXBean();
+        MemoryMXBean mem = ManagementFactory.getMemoryMXBean();
+        ClassLoadingMXBean cl = ManagementFactory.getClassLoadingMXBean();
+        io = context.getIo();
+
+        //
+        // print ServiceMix informations
+        //
+        maxNameLen = 25;
+        io.out.println("ServiceMix");
+        printValue("ServiceMix home", maxNameLen, System.getProperty("servicemix.home"));
+        printValue("ServiceMix base", maxNameLen, System.getProperty("servicemix.base"));
+        printValue("ServiceMix Kernel version", maxNameLen, branding.getParent().getVersion());
+
+        if (branding.getApplicationName() != null && branding.getApplicationVersion() != null) {
+            printValue(branding.getApplicationName() + " version", maxNameLen, branding.getApplicationVersion());
+        }
+        io.out.println();
+
+        io.out.println("JVM");
+        printValue("Java Virtual Machine", maxNameLen, runtime.getVmName() + " version " + runtime.getVmVersion());
+        printValue("Vendor", maxNameLen, runtime.getVmVendor());
+        printValue("Uptime", maxNameLen, printDuration(runtime.getUptime()));
+        try {
+            printValue("Process CPU time", maxNameLen, printDuration(getSunOsValueAsLong(os, "getProcessCpuTime") / 1000000));
+        } catch (Throwable t) {}
+        printValue("Total compile time", maxNameLen, printDuration(ManagementFactory.getCompilationMXBean().getTotalCompilationTime()));
+
+        io.out.println("Threads");
+        printValue("Live threads", maxNameLen, Integer.toString(threads.getThreadCount()));
+        printValue("Daemon threads", maxNameLen, Integer.toString(threads.getDaemonThreadCount()));
+        printValue("Peak", maxNameLen, Integer.toString(threads.getPeakThreadCount()));
+        printValue("Total started", maxNameLen, Long.toString(threads.getTotalStartedThreadCount()));
+
+        io.out.println("Memory");
+        printValue("Current heap size", maxNameLen, printSizeInKb(mem.getHeapMemoryUsage().getUsed()));
+        printValue("Maximum heap size", maxNameLen, printSizeInKb(mem.getHeapMemoryUsage().getMax()));
+        printValue("Committed heap size", maxNameLen, printSizeInKb(mem.getHeapMemoryUsage().getCommitted()));
+        printValue("Pending objects", maxNameLen, Integer.toString(mem.getObjectPendingFinalizationCount()));
+        for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) {
+            String val = "Name = '" + gc.getName() + "', Collections = " + gc.getCollectionCount() + ", Time = " + printDuration(gc.getCollectionTime());
+            printValue("Garbage collector", maxNameLen, val);
+        }
+
+        io.out.println("Classes");
+        printValue("Current classes loaded", maxNameLen, printLong(cl.getLoadedClassCount()));
+        printValue("Total classes loaded", maxNameLen, printLong(cl.getTotalLoadedClassCount()));
+        printValue("Total classes unloaded", maxNameLen, printLong(cl.getUnloadedClassCount()));
+
+        io.out.println("Operating system");
+        printValue("Name", maxNameLen, os.getName() + " version " + os.getVersion());
+        printValue("Architecture", maxNameLen, os.getArch());
+        printValue("Processors", maxNameLen, Integer.toString(os.getAvailableProcessors()));
+        try {
+            printValue("Total physical memory", maxNameLen, printSizeInKb(getSunOsValueAsLong(os, "getTotalPhysicalMemorySize")));
+            printValue("Free physical memory", maxNameLen, printSizeInKb(getSunOsValueAsLong(os, "getFreePhysicalMemorySize")));
+            printValue("Committed virtual memory", maxNameLen, printSizeInKb(getSunOsValueAsLong(os, "getCommittedVirtualMemorySize")));
+            printValue("Total swap space", maxNameLen, printSizeInKb(getSunOsValueAsLong(os, "getTotalSwapSpaceSize")));
+            printValue("Free swap space", maxNameLen, printSizeInKb(getSunOsValueAsLong(os, "getFreeSwapSpaceSize")));
+        } catch (Throwable t) {}
+
+        return null;
+    }
+
+    private long getSunOsValueAsLong(OperatingSystemMXBean os, String name) throws Exception {
+        Method mth = os.getClass().getMethod(name);
+        return (Long) mth.invoke(os);
+    }
+
+    private String printLong(long i) {
+        return fmtI.format(i);
+    }
+
+    private String printSizeInKb(double size) {
+        return fmtI.format((long) (size / 1024)) + " kbytes";
+    }
+
+    private String printDuration(double uptime) {
+        uptime /= 1000;
+        if (uptime < 60) {
+            return fmtD.format(uptime) + " seconds";
+        }
+        uptime /= 60;
+        if (uptime < 60) {
+            long minutes = (long) uptime;
+            String s = fmtI.format(minutes) + (minutes > 1 ? " minutes" : " minute");
+            return s;
+        }
+        uptime /= 60;
+        if (uptime < 24) {
+            long hours = (long) uptime;
+            long minutes = (long) ((uptime - hours) * 60);
+            String s = fmtI.format(hours) + (hours > 1 ? " hours" : " hour");
+            if (minutes != 0) {
+                s += " " + fmtI.format(minutes) + (minutes > 1 ? " minutes" : "minute");
+            }
+            return s;
+        }
+        uptime /= 24;
+        long days = (long) uptime;
+        long hours = (long) ((uptime - days) * 60);
+        String s = fmtI.format(days) + (days > 1 ? " days" : " day");
+        if (hours != 0) {
+            s += " " + fmtI.format(hours) + (hours > 1 ? " hours" : "hour");
+        }
+        return s;
+    }
+
+    void printSysValue(String prop, int pad) {
+        printValue(prop, pad, System.getProperty(prop));
+    }
+
+    void printValue(String name, int pad, String value) {
+        io.out.println("  " + renderer.render(AnsiRenderer.encode(StringUtils.rightPad(name, pad), AnsiCode.BOLD)) + "   " + value);
+    }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/config/CommandParser.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/config/CommandParser.java
new file mode 100644
index 0000000..45d9d7d
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/config/CommandParser.java
@@ -0,0 +1,474 @@
+/*
+ * 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.servicemix.kernel.gshell.core.config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.w3c.dom.Element;
+
+import org.apache.geronimo.gshell.command.Alias;
+import org.apache.geronimo.gshell.command.Link;
+import org.apache.geronimo.gshell.wisdom.command.AliasImpl;
+import org.apache.geronimo.gshell.wisdom.command.ConfigurableCommandCompleter;
+import org.apache.geronimo.gshell.wisdom.command.LinkImpl;
+import org.apache.geronimo.gshell.wisdom.registry.CommandLocationImpl;
+import org.apache.servicemix.kernel.gshell.core.CommandBundle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.GenericBeanDefinition;
+import org.springframework.beans.factory.support.ManagedList;
+import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
+import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.util.StringUtils;
+import org.springframework.util.xml.DomUtils;
+
+public class CommandParser extends AbstractBeanDefinitionParser {
+
+    public static final String ID = ID_ATTRIBUTE;
+
+    public static final String DESCRIPTION = "description";
+
+    public static final String PLUGIN_TEMPLATE = "pluginTemplate";
+
+    public static final String ACTION = "action";
+
+    public static final String ACTION_ID = "actionId";
+
+    public static final String COMMAND_TEMPLATE_SUFFIX = "CommandTemplate";
+
+    public static final String COMMAND_BUNDLE = "command-bundle";
+
+    public static final String NAME = "name";
+
+    public static final String LOCATION = "location";
+
+    public static final String COMMANDS = "commands";
+
+    public static final String COMMAND = "command";
+
+    public static final String TYPE = "type";
+
+    public static final String DOCUMENTER = "documenter";
+
+    public static final String COMPLETER = "completer";
+
+    public static final String COMPLETERS = "completers";
+
+    public static final String BEAN = "bean";
+
+    public static final String REF = "ref";
+
+    public static final String NULL = "null";
+
+    public static final String MESSAGE_SOURCE = "message-source";
+
+    public static final String MESSAGES = "messages";
+
+    public static final String PROTOTYPE = "prototype";
+
+    public static final String ALIAS = "alias";
+
+    public static final String ALIASES = "aliases";
+
+    public static final String LINK = "link";
+
+    public static final String LINKS = "links";
+
+    public static final String TARGET = "target";
+
+    @Override
+    protected boolean shouldGenerateId() {
+        return true;
+    }
+
+    @Override
+    protected boolean shouldGenerateIdAsFallback() {
+        return true;
+    }
+
+    protected AbstractBeanDefinition parseInternal(final Element element, final ParserContext context) {
+        assert element != null;
+        assert context != null;
+
+        Builder builder = new Builder(context);
+        BeanDefinitionBuilder plugin = builder.parseCommandBundle(element);
+        return plugin.getBeanDefinition();
+    }
+
+    /**
+     * Helper to deal with command type.
+     */
+    private enum CommandType
+    {
+        STATELESS,
+        STATEFUL;
+
+        public static CommandType parse(final String text) {
+            assert text != null;
+
+            return valueOf(text.toUpperCase());
+        }
+
+        public String getTemplateName() {
+            return name().toLowerCase() + COMMAND_TEMPLATE_SUFFIX;
+        }
+
+        public void wire(final BeanDefinitionBuilder command, final BeanDefinitionHolder action) {
+            assert command != null;
+            assert action != null;
+
+            switch (this) {
+                case STATELESS:
+                    command.addPropertyReference(ACTION, action.getBeanName());
+                    break;
+
+                case STATEFUL:
+                    command.addPropertyValue(ACTION_ID, action.getBeanName());
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Helper to build plugin related bean definitions.
+     */
+    private class Builder
+    {
+        private final Logger log = LoggerFactory.getLogger(getClass());
+
+        private ParserContext context;
+
+        public Builder(final ParserContext context) {
+            assert context != null;
+
+            this.context = context;
+        }
+
+        private String resolveId(final Element element, final BeanDefinition def) throws BeanDefinitionStoreException {
+            assert element != null;
+            assert def != null;
+
+            if (shouldGenerateId()) {
+                return context.getReaderContext().generateBeanName(def);
+            }
+
+            String id = element.getAttribute(ID_ATTRIBUTE);
+
+            if (!StringUtils.hasText(id) && shouldGenerateIdAsFallback()) {
+                id = context.getReaderContext().generateBeanName(def);
+            }
+
+            return id;
+        }
+
+        @SuppressWarnings({"unchecked"})
+        private List<Element> getChildElements(final Element element, final String name) {
+            assert element != null;
+            assert name != null;
+
+            return DomUtils.getChildElementsByTagName(element, name);
+        }
+
+        @SuppressWarnings({"unchecked"})
+        private List<Element> getChildElements(final Element element, final String[] names) {
+            assert element != null;
+            assert names != null;
+
+            return DomUtils.getChildElementsByTagName(element, names);
+        }
+
+        @SuppressWarnings({"unchecked"})
+        private Element getChildElement(final Element element, final String name) {
+            assert element != null;
+            assert name != null;
+
+            List<Element> elements = DomUtils.getChildElementsByTagName(element, name);
+            if (elements != null && !elements.isEmpty()) {
+                return elements.get(0);
+            }
+            return null;
+        }
+
+        private BeanDefinitionParserDelegate createBeanDefinitionParserDelegate(final Element element) {
+            assert element != null;
+
+            BeanDefinitionParserDelegate parser = new BeanDefinitionParserDelegate(context.getReaderContext());
+            parser.initDefaults(element.getOwnerDocument().getDocumentElement());
+            return parser;
+        }
+
+        private BeanDefinitionHolder parseBeanDefinitionElement(final Element element) {
+            assert element != null;
+
+            BeanDefinitionParserDelegate parser = createBeanDefinitionParserDelegate(element);
+            return parser.parseBeanDefinitionElement(element);
+        }
+
+        private void parseAndApplyDescription(final Element element, final BeanDefinition def) {
+            assert element != null;
+            assert def != null;
+
+            Element desc = getChildElement(element, DESCRIPTION);
+            if (desc != null) {
+                if (def instanceof AbstractBeanDefinition) {
+                    ((AbstractBeanDefinition)def).setDescription(desc.getTextContent());
+                }
+            }
+        }
+
+        private void parseAndApplyDescription(final Element element, final BeanDefinitionBuilder builder) {
+            assert element != null;
+            assert builder != null;
+
+            parseAndApplyDescription(element, builder.getRawBeanDefinition());
+        }
+
+        private BeanDefinitionHolder register(final BeanDefinitionHolder holder) {
+            assert holder != null;
+
+            registerBeanDefinition(holder, context.getRegistry());
+            return holder;
+        }
+
+        private BeanDefinitionHolder register(final BeanDefinition def, final String id) {
+            assert def != null;
+            assert id != null;
+
+            BeanDefinitionHolder holder = new BeanDefinitionHolder(def, id);
+            return register(holder);
+        }
+
+        //
+        // <gshell:command-bundle>
+        //
+
+        private BeanDefinitionBuilder parseCommandBundle(final Element element) {
+            assert element != null;
+
+            log.trace("Parse command bundle; element; {}", element);
+
+            BeanDefinitionBuilder bundle = BeanDefinitionBuilder.rootBeanDefinition(CommandBundle.class);
+            parseAndApplyDescription(element, bundle);
+
+            //
+            // TODO: Figure out how we can save the order of <gshell:command> and <gshell:link> so that 'help' displays them in the order they are defined
+            //
+
+            ManagedList commands = new ManagedList();
+            commands.addAll(parseCommands(element));
+            bundle.addPropertyValue(COMMANDS, commands);
+
+            ManagedList links = new ManagedList();
+            links.addAll(parseLinks(element));
+            bundle.addPropertyValue(LINKS, links);
+
+            ManagedList aliases = new ManagedList();
+            aliases.addAll(parseAliases(element));
+            bundle.addPropertyValue(ALIASES, aliases);
+
+            return bundle;
+        }
+
+        //
+        // <gshell:command>
+        //
+
+        private List<BeanDefinition> parseCommands(final Element element) {
+            assert element != null;
+
+            log.trace("Parse commands; element; {}", element);
+
+            List<BeanDefinition> commands = new ArrayList<BeanDefinition>();
+
+            List<Element> children = getChildElements(element, COMMAND);
+
+            for (Element child : children) {
+                BeanDefinitionBuilder command = parseCommand(child);
+                commands.add(command.getBeanDefinition());
+            }
+
+            return commands;
+        }
+
+        private BeanDefinitionBuilder parseCommand(final Element element) {
+            assert element != null;
+
+            log.trace("Parse command; element; {}", element);
+
+            CommandType type = CommandType.parse(element.getAttribute(TYPE));
+            BeanDefinitionBuilder command = BeanDefinitionBuilder.childBeanDefinition(type.getTemplateName());
+            parseAndApplyDescription(element, command);
+
+            Element child;
+
+            // Required children elements
+
+            String name = element.getAttribute(NAME);
+            BeanDefinition def = new GenericBeanDefinition();
+            def.setBeanClassName(CommandLocationImpl.class.getName());
+            def.getConstructorArgumentValues().addGenericArgumentValue(name);
+            command.addPropertyValue(LOCATION, def);
+
+            child = getChildElement(element, ACTION);
+            BeanDefinitionHolder action = parseCommandAction(child);
+            type.wire(command, action);
+
+            // Optional children elements
+
+            child = getChildElement(element, DOCUMENTER);
+            if (child != null) {
+                BeanDefinitionHolder holder = parseBeanDefinitionElement(child);
+                command.addPropertyValue(DOCUMENTER, holder.getBeanDefinition());
+            }
+
+            child = getChildElement(element, COMPLETER);
+            if (child != null) {
+                BeanDefinitionHolder holder = parseBeanDefinitionElement(child);
+                command.addPropertyValue(COMPLETER, holder.getBeanDefinition());
+            }
+
+            child = getChildElement(element, COMPLETERS);
+            if (child != null) {
+                BeanDefinitionBuilder completer = parseCommandCompleters(child);
+                command.addPropertyValue(COMPLETER, completer.getBeanDefinition());
+            }
+
+            child = getChildElement(element, MESSAGE_SOURCE);
+            if (child != null) {
+                BeanDefinitionHolder holder = parseBeanDefinitionElement(child);
+                command.addPropertyValue(MESSAGES, holder.getBeanDefinition());
+            }
+
+            //String id = resolveId(element, command.getBeanDefinition());
+            //BeanDefinitionHolder holder = register(command.getBeanDefinition(), id);
+
+            return command;
+        }
+
+        //
+        // <gshell:completers>
+        //
+
+        private BeanDefinitionBuilder parseCommandCompleters(final Element element) {
+            assert element != null;
+
+            BeanDefinitionBuilder completer = BeanDefinitionBuilder.rootBeanDefinition(ConfigurableCommandCompleter.class);
+
+            ManagedList completers = new ManagedList();
+
+            List<Element> children = getChildElements(element, new String[] {BEAN, REF, NULL});
+
+            for (Element child : children) {
+                if (DomUtils.nodeNameEquals(child, BEAN)) {
+                    BeanDefinitionHolder holder = parseBeanDefinitionElement(child);
+                    // noinspection unchecked
+                    completers.add(holder.getBeanDefinition());
+                }
+                else if (DomUtils.nodeNameEquals(child, REF)) {
+                    BeanDefinitionParserDelegate parser = createBeanDefinitionParserDelegate(child);
+                    RuntimeBeanReference ref = (RuntimeBeanReference) parser.parsePropertySubElement(child, completer.getRawBeanDefinition());
+                    // noinspection unchecked
+                    completers.add(ref);
+                }
+                else if (DomUtils.nodeNameEquals(child, NULL)) {
+                    // noinspection unchecked
+                    completers.add(null);
+                }
+            }
+
+            completer.addConstructorArgValue(completers);
+
+            return completer;
+        }
+
+        //
+        // <gshell:action>
+        //
+
+        private BeanDefinitionHolder parseCommandAction(final Element element) {
+            assert element != null;
+
+            log.trace("Parse command action; element; {}", element);
+
+            // Construct the action
+            BeanDefinition action = parseBeanDefinitionElement(element).getBeanDefinition();
+
+            // All actions are configured as prototypes
+            action.setScope(PROTOTYPE);
+
+            // Generate id and register the bean
+            String id = resolveId(element, action);
+            return register(action, id);
+        }
+
+        //
+        // <gshell:link>
+        //
+
+        private List<Link> parseLinks(final Element element) {
+            assert element != null;
+
+            log.trace("Parse links; element; {}", element);
+
+            List<Link> links = new ArrayList<Link>();
+
+            List<Element> children = getChildElements(element, LINK);
+
+            for (Element child : children) {
+                String name = child.getAttribute(NAME);
+                String target = child.getAttribute(TARGET);
+
+                links.add(new LinkImpl(name, target));
+            }
+
+            return links;
+        }
+
+        //
+        // <gshell:alias>
+        //
+
+        private List<Alias> parseAliases(final Element element) {
+            assert element != null;
+
+            log.trace("Parse aliases; element; {}", element);
+
+            List<Alias> aliases = new ArrayList<Alias>();
+
+            List<Element> children = getChildElements(element, ALIAS);
+
+            for (Element child : children) {
+                String name = child.getAttribute(NAME);
+                String alias = child.getAttribute(ALIAS);
+
+                aliases.add(new AliasImpl(name, alias));
+            }
+
+            return aliases;
+        }
+    }
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/config/NamespaceHandler.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/config/NamespaceHandler.java
new file mode 100644
index 0000000..3e464d3
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/config/NamespaceHandler.java
@@ -0,0 +1,29 @@
+/*
+ * 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.servicemix.kernel.gshell.core.config;
+
+import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
+
+public class NamespaceHandler extends NamespaceHandlerSupport {
+
+    public void init() {
+        registerBeanDefinitionParser(CommandParser.COMMAND_BUNDLE, new CommandParser());
+    }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/sshd/BogusPasswordAuthenticator.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/sshd/BogusPasswordAuthenticator.java
new file mode 100644
index 0000000..34e2527
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/sshd/BogusPasswordAuthenticator.java
@@ -0,0 +1,29 @@
+/*
+ * 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.servicemix.kernel.gshell.core.sshd;
+
+import org.apache.sshd.server.PasswordAuthenticator;
+
+public class BogusPasswordAuthenticator implements PasswordAuthenticator {
+
+    public Object authenticate(String username, String password) {
+        return (username != null && username.equals(password)) ? username : null;
+    }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/sshd/SshServerFactory.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/sshd/SshServerFactory.java
new file mode 100644
index 0000000..fbf9041
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/sshd/SshServerFactory.java
@@ -0,0 +1,58 @@
+/*
+ * 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.servicemix.kernel.gshell.core.sshd;
+
+import org.apache.sshd.SshServer;
+
+public class SshServerFactory {
+
+    private SshServer server;
+
+    private boolean start;
+
+    public SshServerFactory(SshServer server) {
+        this.server = server;
+    }
+
+    public boolean isStart() {
+        return start;
+    }
+
+    public void setStart(boolean start) {
+        this.start = start;
+    }
+
+    public void start() throws Exception {
+        if (start) {
+            try {
+                server.start();
+            } catch (Exception e) {
+                e.printStackTrace();
+                throw e;
+            }
+        }
+    }
+
+    public void stop() throws Exception {
+        if (start) {
+            server.stop();
+        }
+    }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileObject.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileObject.java
new file mode 100644
index 0000000..41a2bb8
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileObject.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.servicemix.kernel.gshell.core.vfs.mvn;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.commons.httpclient.URIException;
+import org.apache.commons.vfs.FileName;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.provider.URLFileName;
+import org.apache.commons.vfs.provider.url.UrlFileObject;
+
+public class MvnFileObject extends UrlFileObject {
+
+    public MvnFileObject(MvnFileSystem fs, FileName fileName) {
+        super(fs, fileName);
+    }
+
+    protected URL createURL(final FileName name) throws MalformedURLException, FileSystemException, URIException
+    {
+        String url;
+        if (name instanceof URLFileName)
+        {
+            URLFileName urlName = (URLFileName) getName();
+
+            // TODO: charset
+            url = urlName.getURIEncoded(null);
+        }
+        else
+        {
+            url = getName().getURI();
+        }
+        if (url.startsWith("mvn:///")) {
+            url = "mvn:" + url.substring("mvn:///".length());
+        }
+        return new URL(url);
+    }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileProvider.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileProvider.java
new file mode 100644
index 0000000..7d88cd6
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileProvider.java
@@ -0,0 +1,66 @@
+/*
+ * 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.servicemix.kernel.gshell.core.vfs.mvn;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.commons.vfs.FileName;
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSystem;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.FileSystemOptions;
+import org.apache.commons.vfs.provider.url.UrlFileProvider;
+
+public class MvnFileProvider extends UrlFileProvider {
+
+    /**
+     * Locates a file object, by absolute URI.
+     */
+    public synchronized FileObject findFile(final FileObject baseFile,
+                                            final String uri,
+                                            final FileSystemOptions fileSystemOptions)
+        throws FileSystemException
+    {
+        try
+        {
+            final URL url = new URL(uri);
+
+            URL rootUrl = new URL(url, "/");
+            final String key = this.getClass().getName() + rootUrl.toString();
+            FileSystem fs = findFileSystem(key, fileSystemOptions);
+            if (fs == null)
+            {
+                String extForm = rootUrl.toExternalForm();
+                final FileName rootName = getContext().parseURI(extForm);
+                // final FileName rootName =
+                //    new BasicFileName(rootUrl, FileName.ROOT_PATH);
+                fs = new MvnFileSystem(rootName, fileSystemOptions);
+                addFileSystem(key, fs);
+            }
+            return fs.resolveFile(url.getPath());
+        }
+        catch (final MalformedURLException e)
+        {
+            throw new FileSystemException("vfs.provider.url/badly-formed-uri.error", uri, e);
+        }
+    }
+
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileSystem.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileSystem.java
new file mode 100644
index 0000000..7583eb5
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileSystem.java
@@ -0,0 +1,35 @@
+/*
+ * 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.servicemix.kernel.gshell.core.vfs.mvn;
+
+import org.apache.commons.vfs.FileName;
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSystemOptions;
+import org.apache.commons.vfs.provider.url.UrlFileSystem;
+
+public class MvnFileSystem extends UrlFileSystem {
+
+    protected MvnFileSystem(FileName fileName, FileSystemOptions fileSystemOptions) {
+        super(fileName, fileSystemOptions);
+    }
+
+    protected FileObject createFile(FileName fileName) {
+        return new MvnFileObject(this, fileName);
+    }
+}
diff --git a/karaf/gshell/gshell-core/src/main/resources/META-INF/spring.handlers b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring.handlers
new file mode 100644
index 0000000..2315764
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring.handlers
@@ -0,0 +1,24 @@
+##
+## 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.
+##
+
+##
+## $Rev: 697807 $ $Date: 2008-09-22 20:12:04 +0700 (Mon, 22 Sep 2008) $
+##
+
+http\://servicemix.apache.org/schema/servicemix-gshell=org.apache.servicemix.kernel.gshell.core.config.NamespaceHandler
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/META-INF/spring.schemas b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring.schemas
new file mode 100644
index 0000000..67eca68
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring.schemas
@@ -0,0 +1,24 @@
+##
+## 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.
+##
+
+##
+## $Rev: 697807 $ $Date: 2008-09-22 20:12:04 +0700 (Mon, 22 Sep 2008) $
+##
+
+http\://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd=org/apache/servicemix/kernel/gshell/core/servicemix-gshell.xsd
diff --git a/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-commands.xml b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-commands.xml
new file mode 100644
index 0000000..dbcafd3
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-commands.xml
@@ -0,0 +1,277 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+       xsi:schemaLocation="
+  http://www.springframework.org/schema/beans
+  http://www.springframework.org/schema/beans/spring-beans.xsd
+  http://www.springframework.org/schema/util
+  http://www.springframework.org/schema/util/spring-util.xsd
+  http://servicemix.apache.org/schema/servicemix-gshell
+  http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd"
+       default-autowire="no"
+       default-dependency-check="none"
+       default-init-method="init"
+       default-destroy-method="destroy">
+
+    <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+    <bean id="vfsCommandActionTemplate" abstract="true">
+        <property name="fileSystemAccess" ref="fileSystemAccess"/>
+    </bean>
+
+    <gshell:command-bundle>
+        <gshell:command name="about">
+            <gshell:action class="org.apache.geronimo.gshell.commands.builtin.AboutAction">
+                <constructor-arg ref="application"/>
+            </gshell:action>
+        </gshell:command>
+
+        <gshell:command name="help">
+            <gshell:action class="org.apache.geronimo.gshell.commands.builtin.HelpAction">
+                <constructor-arg ref="commandResolver"/>
+            </gshell:action>
+            <gshell:completers>
+                <bean class="org.apache.geronimo.gshell.console.completer.AggregateCompleter">
+                    <constructor-arg>
+                        <list>
+                            <ref bean="aliasNameCompleter"/>
+                            <ref bean="commandNameCompleter"/>
+                        </list>
+                    </constructor-arg>
+                </bean>
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+
+        <gshell:command name="exit">
+            <gshell:action class="org.apache.geronimo.gshell.commands.builtin.ExitAction"/>
+        </gshell:command>
+
+        <gshell:link name="quit" target="exit"/>
+
+        <gshell:command name="echo">
+            <gshell:action class="org.apache.geronimo.gshell.commands.builtin.EchoAction"/>
+        </gshell:command>
+
+        <gshell:command name="clear" type="stateless">
+            <gshell:action class="org.apache.geronimo.gshell.commands.builtin.ClearAction"/>
+        </gshell:command>
+
+        <gshell:command name="source">
+            <gshell:action class="org.apache.geronimo.gshell.commands.builtin.SourceAction">
+                <constructor-arg ref="commandLineExecutor"/>
+                <constructor-arg ref="fileSystemAccess"/>
+            </gshell:action>
+            <gshell:completers>
+                <ref bean="fileObjectNameCompleter"/>
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+
+        <gshell:command name="set">
+            <gshell:action class="org.apache.geronimo.gshell.commands.builtin.SetAction"/>
+        </gshell:command>
+
+        <gshell:command name="unset">
+            <gshell:action class="org.apache.geronimo.gshell.commands.builtin.UnsetAction"/>
+            <gshell:completers>
+                <ref bean="variableNameCompleter"/>
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+
+        <gshell:command name="alias">
+            <gshell:action class="org.apache.geronimo.gshell.commands.builtin.AliasAction">
+                <constructor-arg ref="aliasRegistry"/>
+            </gshell:action>
+        </gshell:command>
+
+        <gshell:command name="unalias">
+            <gshell:action class="org.apache.geronimo.gshell.commands.builtin.UnaliasAction">
+                <constructor-arg ref="aliasRegistry"/>
+            </gshell:action>
+            <gshell:completers>
+                <ref bean="aliasNameCompleter"/>
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+
+        <gshell:command name="history">
+            <gshell:action class="org.apache.geronimo.gshell.commands.builtin.HistoryAction"/>
+        </gshell:command>
+
+        <gshell:command name="info">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.core.commands.InfoAction">
+                <constructor-arg ref="branding" />
+            </gshell:action>
+        </gshell:command>
+    </gshell:command-bundle>
+
+    <gshell:command-bundle>
+        <gshell:command name="ssh">
+            <gshell:action class="org.apache.geronimo.gshell.commands.ssh.SshAction"/>
+        </gshell:command>
+
+        <gshell:command name="sshd">
+            <gshell:action class="org.apache.geronimo.gshell.commands.ssh.SshServerAction"/>
+        </gshell:command>
+    </gshell:command-bundle>
+
+    <gshell:command-bundle>
+        <gshell:command name="exec">
+            <gshell:action class="org.apache.geronimo.gshell.commands.shell.ExecuteAction"/>
+        </gshell:command>
+
+        <gshell:command name="java">
+            <gshell:action class="org.apache.geronimo.gshell.commands.shell.JavaAction"/>
+        </gshell:command>
+
+        <gshell:command name="sleep">
+            <gshell:action class="org.apache.geronimo.gshell.commands.shell.SleepAction"/>
+        </gshell:command>
+
+        <gshell:command name="date">
+            <gshell:action class="org.apache.geronimo.gshell.commands.shell.DateAction"/>
+        </gshell:command>
+
+        <gshell:command name="edit">
+            <gshell:action class="org.apache.geronimo.gshell.commands.shell.EditAction">
+                <property name="fileSystemAccess" ref="fileSystemAccess"/>
+            </gshell:action>
+            <gshell:completers>
+                <ref bean="fileObjectNameCompleter"/>
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+
+        <gshell:command name="find">
+            <gshell:action class="org.apache.geronimo.gshell.commands.shell.FindAction">
+                <property name="fileSystemAccess" ref="fileSystemAccess"/>
+            </gshell:action>
+            <gshell:completers>
+                <ref bean="fileObjectNameCompleter"/>
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+
+        <gshell:command name="sort">
+            <gshell:action class="org.apache.geronimo.gshell.commands.text.SortAction">
+                <property name="fileSystemAccess" ref="fileSystemAccess"/>
+            </gshell:action>
+            <gshell:completers>
+                <ref bean="fileObjectNameCompleter"/>
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+    </gshell:command-bundle>
+
+    <gshell:command-bundle>
+        <gshell:command name="hostname">
+            <gshell:action class="org.apache.geronimo.gshell.commands.network.HostnameAction"/>
+        </gshell:command>
+    </gshell:command-bundle>
+
+    <gshell:command-bundle>
+        <gshell:link name="print" target="echo"/>
+
+        <gshell:command name="printf">
+            <gshell:action class="org.apache.geronimo.gshell.commands.text.PrintfAction"/>
+        </gshell:command>
+
+        <gshell:command name="cat">
+            <gshell:action class="org.apache.geronimo.gshell.commands.text.CatAction">
+                <property name="fileSystemAccess" ref="fileSystemAccess"/>
+            </gshell:action>
+            <gshell:completers>
+                <ref bean="fileObjectNameCompleter"/>
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+
+        <gshell:command name="grep">
+            <gshell:action class="org.apache.geronimo.gshell.commands.text.GrepAction"/>
+        </gshell:command>
+    </gshell:command-bundle>
+
+    <gshell:command-bundle>
+        <gshell:command name="cd">
+            <gshell:action class="org.apache.geronimo.gshell.commands.file.ChangeDirectoryAction" parent="vfsCommandActionTemplate"/>
+            <gshell:completers>
+                <ref bean="fileObjectNameCompleter"/>
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+
+        <gshell:command name="pwd" type="stateless">
+            <gshell:action class="org.apache.geronimo.gshell.commands.file.CurrentDirectoryAction" parent="vfsCommandActionTemplate"/>
+        </gshell:command>
+
+        <gshell:command name="ls">
+            <gshell:action class="org.apache.geronimo.gshell.commands.file.ListDirectoryAction" parent="vfsCommandActionTemplate"/>
+            <gshell:completers>
+                <ref bean="fileObjectNameCompleter"/>
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+
+        <gshell:link name="dir" target="ls"/>
+
+        <gshell:command name="cp">
+            <gshell:action class="org.apache.geronimo.gshell.commands.file.CopyAction" parent="vfsCommandActionTemplate"/>
+            <gshell:completers>
+                <ref bean="fileObjectNameCompleter"/>
+                <ref bean="fileObjectNameCompleter"/>
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+
+        <gshell:link name="copy" target="cp"/>
+
+        <gshell:command name="rm">
+            <gshell:action class="org.apache.geronimo.gshell.commands.file.RemoveAction" parent="vfsCommandActionTemplate"/>
+            <gshell:completers>
+                <ref bean="fileObjectNameCompleter"/>
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+
+        <gshell:link name="del" target="rm"/>
+
+        <gshell:command name="fileinfo">
+            <gshell:action class="org.apache.geronimo.gshell.commands.file.FileInfoAction" parent="vfsCommandActionTemplate"/>
+            <gshell:completers>
+                <ref bean="fileObjectNameCompleter"/>
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+
+        <gshell:command name="touch">
+            <gshell:action class="org.apache.geronimo.gshell.commands.file.TouchAction" parent="vfsCommandActionTemplate"/>
+            <gshell:completers>
+                <ref bean="fileObjectNameCompleter"/>
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+    </gshell:command-bundle>
+
+</beans>
diff --git a/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-local.xml b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-local.xml
new file mode 100644
index 0000000..a047339
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-local.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:osgi="http://www.springframework.org/schema/osgi"
+       xsi:schemaLocation="
+  http://www.springframework.org/schema/beans
+  http://www.springframework.org/schema/beans/spring-beans.xsd
+  http://www.springframework.org/schema/util
+  http://www.springframework.org/schema/util/spring-util.xsd
+  http://www.springframework.org/schema/osgi
+  http://www.springframework.org/schema/osgi/spring-osgi.xsd"
+       default-autowire="no"
+       default-dependency-check="none"
+       default-init-method="init"
+       default-destroy-method="destroy">
+
+    <bean id="localShellWrapped" parent="shell" init-method="init" destroy-method="close"/>
+
+    <bean id="localShell" class="org.apache.servicemix.kernel.gshell.core.ShellWrapper">
+        <constructor-arg ref="localShellWrapped" />
+    </bean>
+
+    <osgi:reference id="mainService" interface="org.apache.servicemix.kernel.main.spi.MainService"/>
+
+    <bean id="localConsole" class="org.apache.servicemix.kernel.gshell.core.LocalConsole">
+        <property name="createLocalShell" value="${servicemix.startLocalConsole}"/>
+        <property name="shell" ref="localShell"/>
+        <property name="mainService" ref="mainService" />
+    </bean>
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-osgi.xml b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-osgi.xml
new file mode 100644
index 0000000..b99626a
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-osgi.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:ctx="http://www.springframework.org/schema/context"
+       xmlns:osgi="http://www.springframework.org/schema/osgi"
+       xmlns:osgix="http://www.springframework.org/schema/osgi-compendium"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="
+  http://www.springframework.org/schema/beans
+  http://www.springframework.org/schema/beans/spring-beans.xsd
+  http://www.springframework.org/schema/context
+  http://www.springframework.org/schema/context/spring-context.xsd
+  http://www.springframework.org/schema/util
+  http://www.springframework.org/schema/util/spring-util.xsd
+  http://www.springframework.org/schema/osgi
+  http://www.springframework.org/schema/osgi/spring-osgi.xsd
+  http://www.springframework.org/schema/osgi-compendium
+  http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd"
+       default-autowire="no"
+       default-dependency-check="none"
+       default-init-method="init"
+       default-destroy-method="destroy">
+
+    <bean id="osgiCommandRegistry" class="org.apache.servicemix.kernel.gshell.core.OsgiCommandRegistry">
+        <constructor-arg ref="commandRegistry" />
+    </bean>
+
+    <bean id="osgiAliasRegistry" class="org.apache.servicemix.kernel.gshell.core.OsgiAliasRegistry">
+        <constructor-arg ref="aliasRegistry" />
+    </bean>
+
+    <osgi:list id="osgiCommands" interface="org.apache.geronimo.gshell.command.Command" cardinality="0..N">
+        <osgi:listener ref="osgiCommandRegistry" bind-method="register" unbind-method="unregister" />
+    </osgi:list>
+
+    <osgi:list id="osgiLinkCommands" interface="org.apache.geronimo.gshell.command.Link" cardinality="0..N">
+        <osgi:listener ref="osgiCommandRegistry" bind-method="register" unbind-method="unregister" />
+    </osgi:list>
+
+    <osgi:list id="osgiAliases" interface="org.apache.geronimo.gshell.command.Alias" cardinality="0..N">
+        <osgi:listener ref="osgiAliasRegistry" bind-method="register" unbind-method="unregister" />
+    </osgi:list>
+
+    <osgi:service ref="localShell" interface="org.apache.geronimo.gshell.shell.Shell">
+    </osgi:service>
+
+    <osgi:service ref="commandLineExecutor" interface="org.apache.geronimo.gshell.commandline.CommandLineExecutor">
+    </osgi:service>
+
+    <osgix:cm-properties id="cmProps" persistent-id="org.apache.servicemix.shell">
+        <prop key="sshPort">8101</prop>
+        <prop key="sshRealm">servicemix</prop>
+        <prop key="hostKey">${servicemix.base}/etc/host.key</prop>
+    </osgix:cm-properties>
+
+    <ctx:property-placeholder properties-ref="cmProps" />
+    
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-remote.xml b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-remote.xml
new file mode 100644
index 0000000..d68ee51
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-remote.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:osgi="http://www.springframework.org/schema/osgi"
+       xmlns:jaas="http://servicemix.apache.org/jaas"
+       xsi:schemaLocation="
+  http://www.springframework.org/schema/beans
+  http://www.springframework.org/schema/beans/spring-beans.xsd
+  http://www.springframework.org/schema/util
+  http://www.springframework.org/schema/util/spring-util.xsd
+  http://www.springframework.org/schema/osgi
+  http://www.springframework.org/schema/osgi/spring-osgi.xsd
+  http://servicemix.apache.org/jaas
+  http://servicemix.apache.org/schema/servicemix-jaas.xsd"
+       default-autowire="no"
+       default-dependency-check="none"
+       default-init-method="init"
+       default-destroy-method="destroy">
+
+    <bean name="sshClient" class="org.apache.sshd.SshClient" factory-method="setUpDefaultClient" init-method="start" destroy-method="stop">
+    </bean>
+
+    <bean name="sshServer" class="org.apache.sshd.SshServer" factory-method="setUpDefaultServer" scope="prototype">
+        <property name="port" value="${sshPort}" />
+        <property name="shellFactory">
+            <bean class="org.apache.geronimo.gshell.commands.ssh.ShellFactoryImpl">
+                <property name="application" ref="application" />
+                <property name="completers">
+                    <list>
+                        <ref bean="commandsCompleter"/>
+                        <ref bean="aliasNameCompleter"/>
+                    </list>
+                </property>
+                <property name="executor" ref="commandLineExecutor" />
+                <property name="prompter">
+                    <bean class="org.apache.geronimo.gshell.wisdom.shell.ConsolePrompterImpl">
+                        <constructor-arg ref="application"/>
+                    </bean>
+                </property>
+                <property name="errorHandler">
+                    <bean class="org.apache.geronimo.gshell.wisdom.shell.ConsoleErrorHandlerImpl" />
+                </property>
+                <property name="history">
+                    <bean class="org.apache.geronimo.gshell.wisdom.shell.HistoryImpl">
+                        <constructor-arg ref="application"/>
+                    </bean>
+                </property>
+            </bean>
+        </property>
+        <property name="keyPairProvider" ref="keyPairProvider" />
+        <property name="passwordAuthenticator" ref="passwordAuthenticator" />
+    </bean>
+
+    <bean name="keyPairProvider" class="org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider">
+        <property name="path" value="${hostKey}" />
+    </bean>
+    <bean name="passwordAuthenticator" class="org.apache.sshd.server.jaas.JaasPasswordAuthenticator">
+        <property name="domain" value="${sshRealm}" />
+    </bean>
+
+    <bean id="sshServerFactory" class="org.apache.servicemix.kernel.gshell.core.sshd.SshServerFactory" init-method="start" destroy-method="stop">
+        <constructor-arg ref="sshServer" />
+        <property name="start" value="${servicemix.startRemoteShell}" />
+    </bean>
+
+    <!--
+    <jaas:config id="SshServer" rank="-1">
+        <jaas:module className="org.apache.geronimo.gshell.remote.server.auth.BogusLoginModule" flags="required" />
+    </jaas:config>
+    -->
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-vfs.xml b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-vfs.xml
new file mode 100644
index 0000000..3992f54
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-vfs.xml
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="
+  http://www.springframework.org/schema/beans
+  http://www.springframework.org/schema/beans/spring-beans.xsd
+  http://www.springframework.org/schema/util
+  http://www.springframework.org/schema/util/spring-util.xsd
+  http://servicemix.apache.org/schema/servicemix-gshell
+  http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd"
+       default-autowire="no"
+       default-dependency-check="none"
+       default-init-method="init"
+       default-destroy-method="destroy">
+
+    <bean id="fileSystemAccess" class="org.apache.geronimo.gshell.vfs.FileSystemAccessImpl">
+        <constructor-arg ref="fileSystemManager"/>
+    </bean>
+
+    <bean id="fileObjectNameCompleter" class="org.apache.geronimo.gshell.vfs.FileObjectNameCompleter">
+        <constructor-arg ref="fileSystemAccess"/>
+    </bean>
+
+    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
+        <property name="customEditors">
+            <map>
+                <entry key="org.apache.commons.vfs.CacheStrategy">
+                    <bean class="org.apache.geronimo.gshell.vfs.config.CacheStrategyEditor"/>
+                </entry>
+            </map>
+        </property>
+    </bean>
+
+    <!--
+    TODO: Set tempDir File arg in constructor to set the temporary location, may need 2 replicators?
+    -->
+    <bean id="defaultFileReplicator" class="org.apache.commons.vfs.impl.DefaultFileReplicator"/>
+
+    <bean id="fileSystemManager" class="org.apache.geronimo.gshell.vfs.config.FileSystemManagerFactoryBean">
+        <property name="filesCache">
+            <bean class="org.apache.commons.vfs.cache.SoftRefFilesCache"/>
+        </property>
+
+        <property name="cacheStrategy" value="ON_RESOLVE"/>
+
+        <property name="fileReplicator">
+            <bean class="org.apache.commons.vfs.impl.PrivilegedFileReplicator">
+                <constructor-arg ref="defaultFileReplicator"/>
+            </bean>
+        </property>
+
+        <!--
+        TODO: Try and root the temporary store under ${gshell.home}/tmp or something
+        -->
+        <property name="temporaryFileStore">
+            <ref bean="defaultFileReplicator"/>
+        </property>
+
+        <property name="fileContentInfoFactory">
+            <bean class="org.apache.commons.vfs.impl.FileContentInfoFilenameFactory"/>
+        </property>
+
+        <property name="defaultProvider">
+            <bean class="org.apache.commons.vfs.provider.url.UrlFileProvider"/>
+        </property>
+    </bean>
+
+    <bean class="org.apache.geronimo.gshell.vfs.config.FileProviderConfigurer">
+        <property name="fileSystemManager" ref="fileSystemManager"/>
+        <property name="scheme" value="tmp"/>
+        <property name="provider">
+            <bean class="org.apache.commons.vfs.provider.temp.TemporaryFileProvider"/>
+        </property>
+    </bean>
+
+    <bean class="org.apache.geronimo.gshell.vfs.config.FileProviderConfigurer">
+        <property name="fileSystemManager" ref="fileSystemManager"/>
+        <property name="scheme" value="ram"/>
+        <property name="provider">
+            <bean class="org.apache.commons.vfs.provider.ram.RamFileProvider"/>
+        </property>
+    </bean>
+
+    <bean class="org.apache.geronimo.gshell.vfs.config.FileProviderConfigurer">
+        <property name="fileSystemManager" ref="fileSystemManager"/>
+        <property name="scheme" value="file"/>
+        <property name="provider">
+            <bean class="org.apache.commons.vfs.provider.local.DefaultLocalFileProvider"/>
+        </property>
+    </bean>
+
+    <bean class="org.apache.geronimo.gshell.vfs.config.FileProviderConfigurer">
+        <property name="fileSystemManager" ref="fileSystemManager"/>
+        <property name="scheme" value="mvn"/>
+        <property name="provider">
+            <bean class="org.apache.servicemix.kernel.gshell.core.vfs.mvn.MvnFileProvider"/>
+        </property>
+    </bean>
+
+    <bean id="metaDataRegistry" class="org.apache.geronimo.gshell.vfs.provider.meta.data.MetaDataRegistryImpl">
+        <constructor-arg ref="eventManager" />
+    </bean>
+
+    <bean id="metaFileProviderConfigurer" class="org.apache.geronimo.gshell.vfs.config.FileProviderConfigurer">
+        <property name="fileSystemManager" ref="fileSystemManager"/>
+        <property name="scheme" value="meta"/>
+        <property name="provider">
+            <bean class="org.apache.geronimo.gshell.vfs.provider.meta.MetaFileProvider">
+                <constructor-arg ref="metaDataRegistry"/>
+            </bean>
+        </property>
+    </bean>
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell.xml b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell.xml
new file mode 100644
index 0000000..8bc494e
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell.xml
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util" xmlns:osgi="http://www.springframework.org/schema/osgi"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+  http://www.springframework.org/schema/beans/spring-beans.xsd
+  http://www.springframework.org/schema/util
+  http://www.springframework.org/schema/util/spring-util.xsd
+  http://servicemix.apache.org/schema/servicemix-gshell
+  http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd
+  http://www.springframework.org/schema/osgi
+  http://www.springframework.org/schema/osgi/spring-osgi-1.0.xsd"
+       default-autowire="no"
+       default-dependency-check="none"
+       default-init-method="init"
+       default-destroy-method="destroy">
+
+    <bean id="io" class="org.apache.geronimo.gshell.io.IO">
+        <property name="verbosity" value="DEBUG"/>
+    </bean>
+
+    <bean id="branding" class="org.apache.servicemix.kernel.gshell.core.ServiceMixBranding">
+        <property name="prompt" value="@|bold %{gshell.username}|@%{application.id}:@|bold %{gshell.group}|> " />
+    </bean>
+
+    <bean id="application" class="org.apache.servicemix.kernel.gshell.core.ApplicationImpl">
+        <property name="id" value="${servicemix.name}"/>
+        <property name="io" ref="io"/>
+        <property name="model">
+            <bean class="org.apache.geronimo.gshell.application.model.ApplicationModel">
+                <property name="branding" ref="branding"/>
+            </bean>
+        </property>
+        <property name="variables">
+            <bean class="org.apache.geronimo.gshell.command.Variables"/>
+        </property>
+    </bean>
+
+    <bean id="eventManager" class="org.apache.geronimo.gshell.event.EventManagerImpl"/>
+
+    <bean id="applicationManager" class="org.apache.servicemix.kernel.gshell.core.ApplicationManagerImpl">
+        <constructor-arg ref="eventManager" />
+        <constructor-arg ref="application" />
+    </bean>
+
+    <bean id="commandLineParser" class="org.apache.geronimo.gshell.parser.CommandLineParser"/>
+
+    <bean id="aliasRegistry" class="org.apache.geronimo.gshell.wisdom.registry.AliasRegistryImpl">
+        <constructor-arg ref="eventManager"/>
+    </bean>
+
+    <bean id="aliasMetaMapper" class="org.apache.geronimo.gshell.wisdom.registry.AliasMetaMapper">
+        <constructor-arg ref="eventManager"/>
+        <constructor-arg ref="metaDataRegistry"/>
+        <constructor-arg ref="aliasRegistry"/>
+    </bean>
+
+    <bean id="commandRegistry" class="org.apache.geronimo.gshell.wisdom.registry.CommandRegistryImpl">
+        <constructor-arg ref="eventManager"/>
+    </bean>
+
+    <bean id="commandMetaMapper" class="org.apache.geronimo.gshell.wisdom.registry.CommandMetaMapper">
+        <constructor-arg ref="eventManager"/>
+        <constructor-arg ref="metaDataRegistry"/>
+        <constructor-arg ref="commandRegistry"/>
+    </bean>
+
+    <bean id="groupDirResolver" class="org.apache.geronimo.gshell.wisdom.registry.GroupDirectoryResolver">
+        <constructor-arg ref="fileSystemAccess"/>
+    </bean>
+
+    <bean id="commandResolver" class="org.apache.geronimo.gshell.wisdom.registry.CommandResolverImpl">
+        <constructor-arg ref="fileSystemAccess"/>
+        <constructor-arg ref="groupDirResolver"/>
+    </bean>
+
+    <bean class="org.apache.servicemix.kernel.gshell.core.WorkAroundAliasCommand" scope="prototype">
+        <constructor-arg ref="commandLineExecutor"/>
+    </bean>
+
+    <bean class="org.apache.geronimo.gshell.wisdom.command.GroupCommand" scope="prototype"/>
+
+    <bean id="commandLineBuilder" class="org.apache.geronimo.gshell.wisdom.shell.CommandLineBuilderImpl">
+        <constructor-arg ref="commandLineParser"/>
+    </bean>
+
+    <bean id="commandLineExecutor" class="org.apache.geronimo.gshell.wisdom.shell.CommandLineExecutorImpl">
+        <constructor-arg ref="commandResolver"/>
+        <constructor-arg ref="commandLineBuilder"/>
+    </bean>
+
+    <bean id="shell" class="org.apache.geronimo.gshell.wisdom.shell.ShellImpl" scope="prototype" init-method="init" destroy-method="close">
+        <constructor-arg ref="application"/>
+        <constructor-arg ref="commandLineExecutor"/>
+
+        <property name="completers">
+            <list>
+                <ref bean="commandsCompleter"/>
+                <ref bean="aliasNameCompleter"/>
+            </list>
+        </property>
+        <property name="prompter">
+            <bean class="org.apache.geronimo.gshell.wisdom.shell.ConsolePrompterImpl">
+                <constructor-arg ref="application"/>
+            </bean>
+        </property>
+        <property name="errorHandler">
+            <bean class="org.apache.geronimo.gshell.wisdom.shell.ConsoleErrorHandlerImpl" />
+        </property>
+        <property name="history">
+            <bean class="org.apache.geronimo.gshell.wisdom.shell.HistoryImpl">
+                <constructor-arg ref="application"/>
+            </bean>
+        </property>
+    </bean>
+
+    <bean id="commandNameCompleter" class="org.apache.geronimo.gshell.wisdom.completer.CommandNameCompleter"
+          lazy-init="true">
+        <constructor-arg ref="eventManager"/>
+        <constructor-arg ref="commandRegistry"/>
+    </bean>
+
+    <bean id="aliasNameCompleter" class="org.apache.geronimo.gshell.wisdom.completer.AliasNameCompleter"
+          lazy-init="true">
+        <constructor-arg ref="eventManager"/>
+        <constructor-arg ref="aliasRegistry"/>
+    </bean>
+
+    <bean id="commandsCompleter" class="org.apache.geronimo.gshell.wisdom.completer.CommandsCompleter" lazy-init="true">
+        <constructor-arg ref="eventManager"/>
+        <constructor-arg ref="commandRegistry"/>
+    </bean>
+
+    <bean id="variableNameCompleter" class="org.apache.geronimo.gshell.wisdom.completer.VariableNameCompleter" lazy-init="true">
+    </bean>
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/org/apache/geronimo/gshell/commands/text/SortAction.properties b/karaf/gshell/gshell-core/src/main/resources/org/apache/geronimo/gshell/commands/text/SortAction.properties
new file mode 100644
index 0000000..e4e787d
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/org/apache/geronimo/gshell/commands/text/SortAction.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Sort lines of text.
+
+command.manual=\
+  TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/commands.xml b/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/commands.xml
new file mode 100644
index 0000000..8b91ee1
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/commands.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="
+  http://www.springframework.org/schema/beans
+  http://www.springframework.org/schema/beans/spring-beans.xsd
+  http://www.springframework.org/schema/util
+  http://www.springframework.org/schema/util/spring-util.xsd">
+
+    <bean class="org.apache.servicemix.kernel.gshell.core.BeanContainerAwareProcessor" />
+
+    <bean id="statelessCommandTemplate" class="org.apache.geronimo.gshell.wisdom.command.StatelessCommand" abstract="true">
+        <property name="documenter">
+            <bean class="org.apache.geronimo.gshell.wisdom.command.MessageSourceCommandDocumenter"/>
+        </property>
+
+        <property name="messages">
+            <bean class="org.apache.geronimo.gshell.wisdom.command.CommandMessageSource"/>
+        </property>
+    </bean>
+
+    <bean id="statefulCommandTemplate" class="org.apache.geronimo.gshell.wisdom.command.StatefulCommand" abstract="true">
+        <property name="documenter">
+            <bean class="org.apache.geronimo.gshell.wisdom.command.MessageSourceCommandDocumenter"/>
+        </property>
+
+        <property name="messages">
+            <bean class="org.apache.geronimo.gshell.wisdom.command.CommandMessageSource"/>
+        </property>
+    </bean>
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/commands/InfoAction.properties b/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/commands/InfoAction.properties
new file mode 100644
index 0000000..755f0c8
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/commands/InfoAction.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Display JVM informations about the current application.
+
+command.manual=\
+  TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/servicemix-gshell.xsd b/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/servicemix-gshell.xsd
new file mode 100644
index 0000000..36a1e1d
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/servicemix-gshell.xsd
@@ -0,0 +1,270 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+
+<!-- $Rev: 699828 $ $Date: 2008-09-28 16:35:27 +0200 (Sun, 28 Sep 2008) $ -->
+
+<xsd:schema xmlns="http://servicemix.apache.org/schema/servicemix-gshell"
+        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+        xmlns:beans="http://www.springframework.org/schema/beans"
+        targetNamespace="http://servicemix.apache.org/schema/servicemix-gshell"
+        elementFormDefault="qualified"
+        attributeFormDefault="unqualified">
+
+    <xsd:import namespace="http://www.springframework.org/schema/beans"/>
+
+    <xsd:annotation>
+        <xsd:documentation>
+            Defines the configuration elements for Apache ServiceMix Kernel commands support.
+        </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:element name="command-bundle">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command bundle.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:sequence>
+                <xsd:element ref="beans:description" minOccurs="0" maxOccurs="1"/>
+                <xsd:choice minOccurs="0" maxOccurs="unbounded">
+					<xsd:element ref="command"/>
+					<xsd:element ref="alias"/>
+                    <xsd:element ref="link"/>
+				</xsd:choice>
+            </xsd:sequence>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:element name="command">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:sequence>
+                <xsd:element ref="beans:description" minOccurs="0" maxOccurs="1"/>
+                <!--
+                NOTE: Not using an xsd:choice here, as I can't seem to figure out how to get it to properly
+                      validate the min/max of the containted elements.  W/o the xsd:choice the validation
+                      works, though have to define elements in order :-(
+                -->
+                <xsd:element ref="action" minOccurs="1" maxOccurs="1"/>
+                <xsd:element ref="documenter" minOccurs="0" maxOccurs="1"/>
+                <xsd:choice minOccurs="0" maxOccurs="1">
+                    <xsd:element ref="completer"/>
+                    <xsd:element ref="completers"/>
+                </xsd:choice>
+                <xsd:element ref="message-source" minOccurs="0" maxOccurs="1"/>
+            </xsd:sequence>
+            <xsd:attribute name="name" type="xsd:string" use="required"/>
+            <xsd:attribute name="type" use="optional" default="stateful">
+                <xsd:annotation>
+                    <xsd:documentation>
+                        The command type.
+                    </xsd:documentation>
+                </xsd:annotation>
+                <xsd:simpleType>
+                    <xsd:restriction base="xsd:string">
+                        <xsd:enumeration value="stateless"/>
+                        <xsd:enumeration value="stateful"/>
+                    </xsd:restriction>
+                </xsd:simpleType>
+            </xsd:attribute>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:element name="link">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a link command.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:sequence>
+                <xsd:element ref="beans:description" minOccurs="0" maxOccurs="1"/>
+            </xsd:sequence>
+            <xsd:attribute name="name" type="xsd:string" use="required"/>
+            <xsd:attribute name="target" type="xsd:string" use="required"/>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:group name="commandComponentElements">
+        <xsd:annotation>
+            <xsd:documentation>
+                Defines the valid elements for command components.  This is based on beans:beanElements,
+                stripping off the bits which are not valid in the command component context.
+            </xsd:documentation>
+        </xsd:annotation>
+		<xsd:sequence>
+			<xsd:element ref="beans:description" minOccurs="0"/>
+			<xsd:choice minOccurs="0" maxOccurs="unbounded">
+				<xsd:element ref="beans:meta"/>
+				<xsd:element ref="beans:constructor-arg"/>
+				<xsd:element ref="beans:property"/>
+				<xsd:element ref="beans:qualifier"/>
+				<xsd:element ref="beans:lookup-method"/>
+				<xsd:element ref="beans:replaced-method"/>
+				<!--
+				NOTE: This seems to cause schema validation problems... not really sure why
+				<xsd:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded"/>
+				-->
+			</xsd:choice>
+		</xsd:sequence>
+	</xsd:group>
+
+    <xsd:attributeGroup name="commandComponentAttributes">
+        <xsd:annotation>
+            <xsd:documentation>
+                Defines the valid attributes for command components.  This is based on beans:beanAttributes,
+                stripping off the bits which are not valid in the command component context.
+            </xsd:documentation>
+        </xsd:annotation>
+		<xsd:attribute name="class" type="xsd:string"/>
+		<xsd:attribute name="parent" type="xsd:string"/>
+		<xsd:attribute name="autowire" default="default">
+			<xsd:simpleType>
+				<xsd:restriction base="xsd:NMTOKEN">
+					<xsd:enumeration value="default"/>
+					<xsd:enumeration value="no"/>
+					<xsd:enumeration value="byName"/>
+					<xsd:enumeration value="byType"/>
+					<xsd:enumeration value="constructor"/>
+					<xsd:enumeration value="autodetect"/>
+				</xsd:restriction>
+			</xsd:simpleType>
+		</xsd:attribute>
+		<xsd:attribute name="dependency-check" default="default">
+			<xsd:simpleType>
+				<xsd:restriction base="xsd:NMTOKEN">
+					<xsd:enumeration value="default"/>
+					<xsd:enumeration value="none"/>
+					<xsd:enumeration value="simple"/>
+					<xsd:enumeration value="objects"/>
+					<xsd:enumeration value="all"/>
+				</xsd:restriction>
+			</xsd:simpleType>
+		</xsd:attribute>
+		<xsd:attribute name="depends-on" type="xsd:string"/>
+		<xsd:attribute name="autowire-candidate" default="default" type="beans:defaultable-boolean"/>
+		<xsd:attribute name="primary" type="xsd:boolean"/>
+		<xsd:attribute name="init-method" type="xsd:string"/>
+		<xsd:attribute name="destroy-method" type="xsd:string"/>
+		<xsd:attribute name="factory-method" type="xsd:string"/>
+		<xsd:attribute name="factory-bean" type="xsd:string"/>
+		<xsd:anyAttribute namespace="##other" processContents="lax"/>
+	</xsd:attributeGroup>
+
+    <xsd:complexType name="commandComponent" abstract="true">
+        <xsd:annotation>
+            <xsd:documentation>
+                Support for command component elements, which are all basically just beans.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:group ref="commandComponentElements"/>
+        <xsd:attributeGroup ref="commandComponentAttributes"/>
+    </xsd:complexType>
+
+    <xsd:element name="action">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command action.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:complexContent>
+                <xsd:extension base="commandComponent"/>
+            </xsd:complexContent>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:element name="documenter">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command documenter.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:complexContent>
+                <xsd:extension base="commandComponent"/>
+            </xsd:complexContent>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:element name="completer">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command completer.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:complexContent>
+                <xsd:extension base="commandComponent"/>
+            </xsd:complexContent>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:element name="completers">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a configurable command completer with a set of completers.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:sequence>
+                <xsd:choice minOccurs="1" maxOccurs="unbounded">
+                    <xsd:element ref="beans:bean"/>
+                    <xsd:element ref="beans:ref"/>
+                    <xsd:element ref="beans:null"/>
+                </xsd:choice>
+            </xsd:sequence>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:element name="message-source">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command message source.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:complexContent>
+                <xsd:extension base="commandComponent"/>
+            </xsd:complexContent>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:element name="alias">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command alias.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:sequence>
+                <xsd:element ref="beans:description" minOccurs="0" maxOccurs="1"/>
+            </xsd:sequence>
+            <xsd:attribute name="name" type="xsd:string" use="required"/>
+            <xsd:attribute name="alias" type="xsd:string" use="required"/>
+        </xsd:complexType>
+    </xsd:element>
+
+</xsd:schema>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/test/java/org/apache/geronimo/gshell/commands/text/SortTest.java b/karaf/gshell/gshell-core/src/test/java/org/apache/geronimo/gshell/commands/text/SortTest.java
new file mode 100644
index 0000000..90e405e
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/test/java/org/apache/geronimo/gshell/commands/text/SortTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.geronimo.gshell.commands.text;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Collections;
+
+import junit.framework.TestCase;
+
+/**
+ * TODO: remove this file when gshell is upgraded
+ */
+public class SortTest extends TestCase {
+
+    public void testFieldIndexesDefaultSep() {
+        SortAction.SortComparator comparator = new SortAction.SortComparator(false, false, false, false, '\0', null);
+        List<Integer> indexes = comparator.getFieldIndexes(" ad  re  t ");
+        assertTrue(Arrays.asList(0, 2, 3, 6, 7, 9, 10, 10).equals(indexes));
+    }
+
+    public void testFieldIndexesWithSep() {
+        SortAction.SortComparator comparator = new SortAction.SortComparator(false, false, false, false, '[', null);
+        List<Integer> indexes = comparator.getFieldIndexes("[  10] [Active     ] [       ] [    8] OPS4J Pax Logging - Service (1.3.0)");
+        assertTrue(Arrays.asList(1, 6, 8, 20, 22, 30, 32, 73 ).equals(indexes));
+
+        indexes = comparator.getFieldIndexes(" ad  re  t ");
+        assertTrue(Arrays.asList(0, 10).equals(indexes));
+    }
+
+    public void testSort() {
+        String s0 = "0321   abcd  ddcba   a";
+        String s1 = " 57t   bcad  ddacb   b";
+        String s2 = "  128  cab   ddbac   c";
+        List<String> strings = Arrays.asList(s0, s1, s2);
+
+        Collections.sort(strings, new SortAction.SortComparator(false, false, false, false, '\0', Arrays.asList("2")));
+        assertTrue(Arrays.asList(s0, s1, s2).equals(strings));
+
+        Collections.sort(strings, new SortAction.SortComparator(false, false, false, false, '\0', Arrays.asList("2.2b")));
+        assertTrue(Arrays.asList(s2, s0, s1).equals(strings));
+
+        Collections.sort(strings, new SortAction.SortComparator(false, false, false, true, '\0', null));
+        assertTrue(Arrays.asList(s1, s2, s0).equals(strings));
+    }
+
+}
diff --git a/karaf/gshell/gshell-core/src/test/java/org/apache/servicemix/kernel/gshell/core/Test.java b/karaf/gshell/gshell-core/src/test/java/org/apache/servicemix/kernel/gshell/core/Test.java
new file mode 100644
index 0000000..0983b12
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/test/java/org/apache/servicemix/kernel/gshell/core/Test.java
@@ -0,0 +1,154 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import junit.framework.TestCase;
+import org.apache.geronimo.gshell.application.ApplicationManager;
+import org.apache.geronimo.gshell.shell.Shell;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+public class Test extends TestCase {
+
+    public void test() throws Exception {
+        System.setProperty("startLocalConsole", "true");
+        System.setProperty("servicemix.name", "root");
+
+        ClassPathXmlApplicationContext context = null;
+        try {
+            context = new ClassPathXmlApplicationContext(
+                    new String[] { "META-INF/spring/gshell.xml",
+                                   "META-INF/spring/gshell-vfs.xml",
+                                   "META-INF/spring/gshell-commands.xml",
+                                   "org/apache/servicemix/kernel/gshell/core/gshell-test.xml" });
+            ApplicationManager appMgr = (ApplicationManager) context.getBean("applicationManager");
+            assertNotNull(appMgr);
+            Shell shell = appMgr.create();
+            assertNotNull(shell);
+            shell.execute("help");
+        } finally {
+            if (context != null) {
+                context.destroy();
+            }
+        }
+    }
+
+    public void testBanner() throws Exception {
+        System.setProperty("startLocalConsole", "true");
+        System.setProperty("servicemix.name", "root");
+
+        ClassPathXmlApplicationContext context = null;
+        try {
+            context = new ClassPathXmlApplicationContext(
+                    new String[] { "META-INF/spring/gshell.xml",
+                                   "META-INF/spring/gshell-vfs.xml",
+                                   "META-INF/spring/gshell-commands.xml",
+                                   "org/apache/servicemix/kernel/gshell/core/gshell-test.xml"});
+            ApplicationManager appMgr = (ApplicationManager) context.getBean("applicationManager");
+            assertNotNull(appMgr);
+            Shell shell = appMgr.create();
+            ServiceMixBranding smxBrandng = (ServiceMixBranding)appMgr.getApplication().getModel().getBranding();
+            assertNotNull(smxBrandng.getWelcomeMessage());
+            System.out.println(smxBrandng.getWelcomeMessage());
+            assertNotNull(shell);
+            shell.execute("about");
+        } finally {
+            if (context != null) {
+                context.destroy();
+            }
+        }
+    }
+
+    public void testLs() throws Exception {
+        System.setProperty("startLocalConsole", "true");
+        System.setProperty("servicemix.name", "root");
+
+        ClassPathXmlApplicationContext context = null;
+        try {
+            context = new ClassPathXmlApplicationContext(
+                    new String[] { "META-INF/spring/gshell.xml",
+                                   "META-INF/spring/gshell-vfs.xml",
+                                   "org/apache/servicemix/kernel/gshell/core/gshell-test-commands.xml",
+                                   "org/apache/servicemix/kernel/gshell/core/gshell-test.xml"});
+            ApplicationManager appMgr = (ApplicationManager) context.getBean("applicationManager");
+            assertNotNull(appMgr);
+            Shell shell = appMgr.create();
+            ServiceMixBranding smxBrandng = (ServiceMixBranding)appMgr.getApplication().getModel().getBranding();
+            assertNotNull(smxBrandng.getWelcomeMessage());
+            System.out.println(smxBrandng.getWelcomeMessage());
+            assertNotNull(shell);
+            shell.execute("vfs/ls meta:/commands/");
+        } finally {
+            if (context != null) {
+                context.destroy();
+            }
+        }
+    }
+
+    public void testCommandGroups() throws Exception {
+        System.setProperty("startLocalConsole", "true");
+        System.setProperty("servicemix.name", "root");
+
+        ClassPathXmlApplicationContext context = null;
+        try {
+            context = new ClassPathXmlApplicationContext(
+                    new String[] { "META-INF/spring/gshell.xml",
+                                   "META-INF/spring/gshell-vfs.xml",
+                                   "org/apache/servicemix/kernel/gshell/core/gshell-test-commands.xml",
+                                   "org/apache/servicemix/kernel/gshell/core/gshell-test.xml"});
+            ApplicationManager appMgr = (ApplicationManager) context.getBean("applicationManager");
+            assertNotNull(appMgr);
+            Shell shell = appMgr.create();
+            ServiceMixBranding smxBrandng = (ServiceMixBranding)appMgr.getApplication().getModel().getBranding();
+            assertNotNull(smxBrandng.getWelcomeMessage());
+            System.out.println(smxBrandng.getWelcomeMessage());
+            assertNotNull(shell);
+
+            shell.execute("vfs");
+            shell.execute("help");
+            shell.execute("..");
+        } finally {
+            if (context != null) {
+                context.destroy();
+            }
+        }
+    }
+
+    public void testFileAccessCommands() throws Exception {
+        System.setProperty("startLocalConsole", "true");
+        System.setProperty("servicemix.name", "root");
+
+        ClassPathXmlApplicationContext context = null;
+        try {
+            context = new ClassPathXmlApplicationContext(
+                    new String[] { "META-INF/spring/gshell.xml",
+                                   "META-INF/spring/gshell-vfs.xml",
+                                   "org/apache/servicemix/kernel/gshell/core/gshell-test-commands.xml",
+                                   "org/apache/servicemix/kernel/gshell/core/gshell-test.xml"});
+            ApplicationManager appMgr = (ApplicationManager) context.getBean("applicationManager");
+            assertNotNull(appMgr);
+            Shell shell = appMgr.create();            
+            assertNotNull(shell);
+            shell.execute("optional/cat src/test/resources/org/apache/servicemix/kernel/gshell/core/gshell-test.xml");
+            shell.execute("optional/find src/test/resources/org/apache/servicemix/kernel/gshell/core/gshell-test.xml");
+        } finally {
+            if (context != null) {
+                context.destroy();
+            }
+        }
+    }
+
+}
diff --git a/karaf/gshell/gshell-core/src/test/resources/log4j.properties b/karaf/gshell/gshell-core/src/test/resources/log4j.properties
new file mode 100644
index 0000000..8cc85f8
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/test/resources/log4j.properties
@@ -0,0 +1,33 @@
+################################################################################
+#
+#    Licensed to the Apache Software Foundation (ASF) under one or more
+#    contributor license agreements.  See the NOTICE file distributed with
+#    this work for additional information regarding copyright ownership.
+#    The ASF licenses this file to You under the Apache License, Version 2.0
+#    (the "License"); you may not use this file except in compliance with
+#    the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+#
+################################################################################
+
+# Root logger
+log4j.rootLogger=INFO, stdout
+
+# CONSOLE appender not used by default
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+
+# File appender
+log4j.appender.out=org.apache.log4j.FileAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+log4j.appender.out.file=${servicemix.base}/data/log/servicemix.log
+log4j.appender.out.append=true
diff --git a/karaf/gshell/gshell-core/src/test/resources/org/apache/servicemix/kernel/gshell/core/gshell-test-commands.xml b/karaf/gshell/gshell-core/src/test/resources/org/apache/servicemix/kernel/gshell/core/gshell-test-commands.xml
new file mode 100644
index 0000000..25e4de1
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/test/resources/org/apache/servicemix/kernel/gshell/core/gshell-test-commands.xml
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+       xsi:schemaLocation="
+  http://www.springframework.org/schema/beans
+  http://www.springframework.org/schema/beans/spring-beans.xsd
+  http://www.springframework.org/schema/util
+  http://www.springframework.org/schema/util/spring-util.xsd
+  http://servicemix.apache.org/schema/servicemix-gshell
+  http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd"
+       default-autowire="no"
+       default-dependency-check="none"
+       default-init-method="init"
+       default-destroy-method="destroy">
+
+    <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+    <bean id="vfsCommandActionTemplate" abstract="true">
+        <property name="fileSystemAccess" ref="fileSystemAccess"/>
+    </bean>
+
+    <gshell:command-bundle>
+        <gshell:command name="about">
+            <gshell:action class="org.apache.geronimo.gshell.commands.builtin.AboutAction">
+                <constructor-arg ref="application"/>
+            </gshell:action>
+        </gshell:command>
+
+        <gshell:command name="help">
+            <gshell:action class="org.apache.geronimo.gshell.commands.builtin.HelpAction">
+                <constructor-arg ref="commandResolver"/>
+            </gshell:action>
+            <gshell:completers>
+                <bean class="org.apache.geronimo.gshell.console.completer.AggregateCompleter">
+                    <constructor-arg>
+                        <list>
+                            <ref bean="aliasNameCompleter"/>
+                            <ref bean="commandNameCompleter"/>
+                        </list>
+                    </constructor-arg>
+                </bean>
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+
+        <gshell:command name="exit">
+            <gshell:action class="org.apache.geronimo.gshell.commands.builtin.ExitAction"/>
+        </gshell:command>
+
+        <gshell:link name="quit" target="exit"/>
+
+        <gshell:command name="echo">
+            <gshell:action class="org.apache.geronimo.gshell.commands.builtin.EchoAction"/>
+        </gshell:command>
+
+        <gshell:command name="clear" type="stateless">
+            <gshell:action class="org.apache.geronimo.gshell.commands.builtin.ClearAction"/>
+        </gshell:command>
+
+        <gshell:command name="source">
+            <gshell:action class="org.apache.geronimo.gshell.commands.builtin.SourceAction">
+                <constructor-arg ref="commandLineExecutor"/>
+                <constructor-arg ref="fileSystemAccess"/>
+            </gshell:action>
+            <gshell:completers>
+                <ref bean="fileObjectNameCompleter"/>
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+
+        <gshell:command name="set">
+            <gshell:action class="org.apache.geronimo.gshell.commands.builtin.SetAction"/>
+        </gshell:command>
+
+        <gshell:command name="unset">
+            <gshell:action class="org.apache.geronimo.gshell.commands.builtin.UnsetAction"/>
+            <gshell:completers>
+                <ref bean="variableNameCompleter"/>
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+
+        <gshell:command name="alias">
+            <gshell:action class="org.apache.geronimo.gshell.commands.builtin.AliasAction">
+                <constructor-arg ref="aliasRegistry"/>
+            </gshell:action>
+        </gshell:command>
+
+        <gshell:command name="unalias">
+            <gshell:action class="org.apache.geronimo.gshell.commands.builtin.UnaliasAction">
+                <constructor-arg ref="aliasRegistry"/>
+            </gshell:action>
+            <gshell:completers>
+                <ref bean="aliasNameCompleter"/>
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+
+        <gshell:command name="history">
+            <gshell:action class="org.apache.geronimo.gshell.commands.builtin.HistoryAction"/>
+        </gshell:command>
+
+        <gshell:command name="info">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.core.commands.InfoAction">
+                <constructor-arg ref="branding" />
+            </gshell:action>
+        </gshell:command>
+    </gshell:command-bundle>
+
+    <gshell:command-bundle>
+        <gshell:command name="vfs/ls">
+            <gshell:action class="org.apache.geronimo.gshell.commands.file.ListDirectoryAction" parent="vfsCommandActionTemplate"/>
+            <gshell:completers>
+                <ref bean="fileObjectNameCompleter"/>
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+   </gshell:command-bundle>
+
+    <gshell:command-bundle>
+        <gshell:command name="optional/cat">
+            <gshell:action class="org.apache.geronimo.gshell.commands.text.CatAction">
+                <property name="fileSystemAccess" ref="fileSystemAccess"/>
+            </gshell:action>
+            <gshell:completers>
+                <ref bean="fileObjectNameCompleter"/>
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+
+        <gshell:command name="optional/find">
+            <gshell:action class="org.apache.geronimo.gshell.commands.shell.FindAction">
+                <property name="fileSystemAccess" ref="fileSystemAccess"/>
+            </gshell:action>
+            <gshell:completers>
+                <ref bean="fileObjectNameCompleter"/>
+                <null/>
+            </gshell:completers>
+        </gshell:command>
+    </gshell:command-bundle>
+
+</beans>
diff --git a/karaf/gshell/gshell-core/src/test/resources/org/apache/servicemix/kernel/gshell/core/gshell-test.xml b/karaf/gshell/gshell-core/src/test/resources/org/apache/servicemix/kernel/gshell/core/gshell-test.xml
new file mode 100644
index 0000000..127543f
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/test/resources/org/apache/servicemix/kernel/gshell/core/gshell-test.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+       xsi:schemaLocation="
+  http://www.springframework.org/schema/beans
+  http://www.springframework.org/schema/beans/spring-beans.xsd
+  http://www.springframework.org/schema/util
+  http://www.springframework.org/schema/util/spring-util.xsd
+  http://servicemix.apache.org/schema/servicemix-gshell
+  http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" />
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/pom.xml b/karaf/gshell/gshell-features/pom.xml
new file mode 100644
index 0000000..f53d9a8
--- /dev/null
+++ b/karaf/gshell/gshell-features/pom.xml
@@ -0,0 +1,114 @@
+<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">
+
+    <!--
+
+        Licensed to the Apache Software Foundation (ASF) under one or more
+        contributor license agreements.  See the NOTICE file distributed with
+        this work for additional information regarding copyright ownership.
+        The ASF licenses this file to You under the Apache License, Version 2.0
+        (the "License"); you may not use this file except in compliance with
+        the License.  You may obtain a copy of the License at
+
+           http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.servicemix.kernel.gshell</groupId>
+        <artifactId>gshell</artifactId>
+        <version>1.2.0-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.apache.servicemix.kernel.gshell</groupId>
+    <artifactId>org.apache.servicemix.kernel.gshell.features</artifactId>
+    <packaging>bundle</packaging>
+    <version>1.2.0-SNAPSHOT</version>
+    <name>Apache ServiceMix Kernel :: GShell Features</name>
+
+    <description>
+        Provides Features in GShell
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.bundlerepository</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.servicemix.kernel.gshell</groupId>
+            <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.servicemix.kernel.gshell</groupId>
+            <artifactId>org.apache.servicemix.kernel.gshell.obr</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.servicemix.kernel</groupId>
+            <artifactId>org.apache.servicemix.kernel.filemonitor</artifactId>
+        </dependency>
+        
+        <dependency>
+            <groupId>org.springframework.osgi</groupId>
+            <artifactId>spring-osgi-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.easymock</groupId>
+            <artifactId>easymock</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+                        <Export-Package>org.apache.servicemix.kernel.gshell.features*;version=${project.version}
+                        </Export-Package>
+                        <Import-Package>
+                            org.apache.geronimo.gshell.wisdom.command,
+                            org.apache.servicemix.kernel.gshell.core,
+                            org.apache.geronimo.gshell.wisdom.registry,
+                            org.springframework.beans.factory.config,
+                            *
+                        </Import-Package>
+                        <Private-Package>!*</Private-Package>
+                        <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/Feature.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/Feature.java
new file mode 100644
index 0000000..45e8aa1
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/Feature.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.features;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A feature is a list of bundles associated identified by its name.
+ */
+public interface Feature {
+
+    String getId();
+
+    String getName();
+
+    String getVersion();
+
+    List<Feature> getDependencies();
+
+    List<String> getBundles();
+
+    Map<String, Map<String, String>> getConfigurations();
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/FeaturesRegistry.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/FeaturesRegistry.java
new file mode 100644
index 0000000..33e1f6e
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/FeaturesRegistry.java
@@ -0,0 +1,35 @@
+/*
+ * 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.servicemix.kernel.gshell.features;
+
+/**
+ * Main interface for a Feature Registry which tracks available and installed features.
+ * Tracks features and repositories.
+ */
+public interface FeaturesRegistry {
+    void register(Feature feature);
+
+    void unregister(Feature feature);
+
+    void registerInstalled(Feature feature);
+
+    void unregisterInstalled(Feature feature);
+
+    void register(Repository repository);
+
+    void unregister(Repository repository);
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/FeaturesService.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/FeaturesService.java
new file mode 100644
index 0000000..1226016
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/FeaturesService.java
@@ -0,0 +1,44 @@
+/*
+ * 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.servicemix.kernel.gshell.features;
+
+import java.net.URI;
+
+/**
+ * The service managing features repositories.
+ */
+public interface FeaturesService {
+
+    void addRepository(URI url) throws Exception;
+
+    void removeRepository(URI url);
+
+    Repository[] listRepositories();
+
+    void installFeature(String name) throws Exception;
+    
+    void installFeature(String name, String version) throws Exception;
+
+    void uninstallFeature(String name) throws Exception;
+    
+    void uninstallFeature(String name, String version) throws Exception;
+
+    String[] listFeatures() throws Exception;
+
+    String[] listInstalledFeatures();
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/Repository.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/Repository.java
new file mode 100644
index 0000000..2bfbc99
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/Repository.java
@@ -0,0 +1,32 @@
+/*
+ * 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.servicemix.kernel.gshell.features;
+
+import java.net.URI;
+
+/**
+ * A repository of features.
+ */
+public interface Repository {
+
+    URI getURI();
+
+    URI[] getRepositories() throws Exception;
+
+    Feature[] getFeatures() throws Exception;
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/AddUrlCommand.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/AddUrlCommand.java
new file mode 100644
index 0000000..213e1e5
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/AddUrlCommand.java
@@ -0,0 +1,35 @@
+/*
+ * 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.servicemix.kernel.gshell.features.commands;
+
+import java.net.URI;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+
+public class AddUrlCommand extends FeaturesCommandSupport {
+
+    @Argument(required = true, multiValued = true, description = "Repository URLs")
+    List<String> urls;
+
+    protected void doExecute(FeaturesService admin) throws Exception {
+        for (String url : urls) {
+            admin.addRepository(new URI(url));
+        }
+    }
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/FeaturesCommandSupport.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/FeaturesCommandSupport.java
new file mode 100644
index 0000000..06e0636
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/FeaturesCommandSupport.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.features.commands;
+
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+import org.osgi.framework.ServiceReference;
+
+public abstract class FeaturesCommandSupport extends OsgiCommandSupport {
+
+    protected Object doExecute() throws Exception {
+        // Get repository admin service.
+        ServiceReference ref = getBundleContext().getServiceReference(FeaturesService.class.getName());
+        if (ref == null) {
+            io.out.println("FeaturesService service is unavailable.");
+            return null;
+        }
+        try {
+            FeaturesService admin = (FeaturesService) getBundleContext().getService(ref);
+            if (admin == null) {
+                io.out.println("FeaturesService service is unavailable.");
+                return null;
+            }
+
+            doExecute(admin);
+        }
+        finally {
+            getBundleContext().ungetService(ref);
+        }
+        return null;
+    }
+
+    protected abstract void doExecute(FeaturesService admin) throws Exception;
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/InstallFeatureCommand.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/InstallFeatureCommand.java
new file mode 100644
index 0000000..a170f3c
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/InstallFeatureCommand.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.features.commands;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+
+public class InstallFeatureCommand extends FeaturesCommandSupport {
+
+    @Argument(required = true, description = "The name of the feature")
+    String name;
+    @Argument(description = "The version of the feature", index = 1)
+    String version;
+
+    protected void doExecute(FeaturesService admin) throws Exception {
+    	if (version != null && version.length() > 0) {
+    		admin.installFeature(name, version);
+    	} else {
+    		admin.installFeature(name);
+    	}
+    }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/ListFeaturesCommand.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/ListFeaturesCommand.java
new file mode 100644
index 0000000..fba0475
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/ListFeaturesCommand.java
@@ -0,0 +1,48 @@
+/*
+ * 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.servicemix.kernel.gshell.features.commands;
+
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+
+public class ListFeaturesCommand extends FeaturesCommandSupport {
+
+    @Option(name = "-i", aliases={"--installed"}, description="Display the list of installed features")
+    boolean installed;
+
+    protected void doExecute(FeaturesService admin) throws Exception {
+        String[] features;
+        if (installed) {
+            features = admin.listInstalledFeatures();
+        } else {
+        	// Print column headers.
+        	io.out.println("  State          Version       Name");
+            features = admin.listFeatures();
+        }
+        if ((features != null) && (features.length > 0)) {
+            for (int i = 0; i < features.length; i++) {
+                io.out.println(features[i]);
+            }
+        } else {
+            if (installed) {
+                io.out.println("No features installed.");
+            } else {
+                io.out.println("No features available.");
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/ListUrlCommand.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/ListUrlCommand.java
new file mode 100644
index 0000000..4c84e09
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/ListUrlCommand.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.features.commands;
+
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+import org.apache.servicemix.kernel.gshell.features.Repository;
+
+public class ListUrlCommand extends FeaturesCommandSupport {
+
+    protected void doExecute(FeaturesService admin) throws Exception {
+        Repository[] repos = admin.listRepositories();
+        if ((repos != null) && (repos.length > 0)) {
+            for (int i = 0; i < repos.length; i++) {
+                io.out.println(repos[i].getURI());
+            }
+        } else {
+            io.out.println("No repository URLs are set.");
+        }
+    }
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/RefreshUrlCommand.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/RefreshUrlCommand.java
new file mode 100644
index 0000000..cd594b1
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/RefreshUrlCommand.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.features.commands;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+import org.apache.servicemix.kernel.gshell.features.Repository;
+
+public class RefreshUrlCommand extends FeaturesCommandSupport {
+
+    @Argument(required = false, multiValued = true, description = "Repository URLs (leave empty for all)")
+    List<String> urls;
+
+    protected void doExecute(FeaturesService admin) throws Exception {
+        if (urls == null || urls.isEmpty()) {
+            urls = new ArrayList<String>();
+            for (Repository repo : admin.listRepositories()) {
+                urls.add(repo.getURI().toString());
+            }
+        }
+        for (String strUri : urls) {
+            URI uri = new URI(strUri);
+            admin.removeRepository(uri);
+            admin.addRepository(uri);
+        }
+    }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/RemoveUrlCommand.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/RemoveUrlCommand.java
new file mode 100644
index 0000000..d7e6e01
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/RemoveUrlCommand.java
@@ -0,0 +1,35 @@
+/*
+ * 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.servicemix.kernel.gshell.features.commands;
+
+import java.net.URI;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+
+public class RemoveUrlCommand extends FeaturesCommandSupport {
+
+    @Argument(required = true, multiValued = true, description = "Repository URLs")
+    List<String> urls;
+
+    protected void doExecute(FeaturesService admin) throws Exception {
+        for (String url : urls) {
+            admin.removeRepository(new URI(url));
+        }
+    }
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/UninstallFeatureCommand.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/UninstallFeatureCommand.java
new file mode 100644
index 0000000..522c721
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/UninstallFeatureCommand.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.features.commands;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+
+public class UninstallFeatureCommand extends FeaturesCommandSupport {
+
+    @Argument(required = true, description = "The name of the feature")
+    String name;
+    @Argument(description = "The version of the feature", index = 1)
+    String version;
+
+    protected void doExecute(FeaturesService admin) throws Exception {
+    	if (version != null && version.length() > 0) {
+    		admin.uninstallFeature(name, version);
+    	} else {
+    		admin.uninstallFeature(name );
+    	}
+    }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/AvailableFeatureCompleter.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/AvailableFeatureCompleter.java
new file mode 100644
index 0000000..991ba59
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/AvailableFeatureCompleter.java
@@ -0,0 +1,55 @@
+/*
+ * 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.servicemix.kernel.gshell.features.completers;
+
+import java.util.Collection;
+import java.util.List;
+
+import jline.Completor;
+import org.apache.geronimo.gshell.console.completer.StringsCompleter;
+import org.apache.servicemix.kernel.gshell.features.management.ManagedFeature;
+import org.apache.servicemix.kernel.gshell.features.management.ManagedFeaturesRegistry;
+
+/**
+ * {@link jline.Completor} for available features.
+ *
+ * Displays a list of available features from installed repositories.
+ *
+ */
+public class AvailableFeatureCompleter implements Completor {
+
+    private ManagedFeaturesRegistry featuresRegistry;
+    private StringsCompleter delegate;
+
+    public void setFeaturesRegistry(ManagedFeaturesRegistry featuresRegistry) {
+        this.featuresRegistry = featuresRegistry;
+    }
+
+    public int complete(final String buffer, final int cursor, final List candidates) {
+
+        Collection<ManagedFeature> features = featuresRegistry.getAvailableFeatures().values();
+        delegate = new StringsCompleter();
+
+        for (ManagedFeature feature : features) {
+            delegate.getStrings().add(feature.getName());
+        }
+        
+        return delegate.complete(buffer, cursor, candidates);
+    }
+
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/FeatureRepositoryCompleter.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/FeatureRepositoryCompleter.java
new file mode 100644
index 0000000..461ca32
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/FeatureRepositoryCompleter.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.features.completers;
+
+import java.util.List;
+
+import jline.Completor;
+import org.apache.geronimo.gshell.console.completer.StringsCompleter;
+import org.apache.servicemix.kernel.gshell.features.management.ManagedFeaturesRegistry;
+
+/**
+ * {@link jline.Completor} for Feature Repository URLs.
+ *
+ * Displays a list of currently installed Feature repositories.
+ *
+ */
+
+public class FeatureRepositoryCompleter implements Completor {
+
+    private ManagedFeaturesRegistry featuresRegistry;
+
+    public void setFeaturesRegistry(ManagedFeaturesRegistry featuresRegistry) {
+        this.featuresRegistry = featuresRegistry;
+    }
+
+    public int complete(final String buffer, final int cursor, final List candidates) {
+        StringsCompleter delegate = new StringsCompleter(featuresRegistry.getRepositories().keySet());
+        return delegate.complete(buffer, cursor, candidates);
+    }
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/InstalledFeatureCompleter.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/InstalledFeatureCompleter.java
new file mode 100644
index 0000000..6a121a6
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/InstalledFeatureCompleter.java
@@ -0,0 +1,54 @@
+/*
+ * 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.servicemix.kernel.gshell.features.completers;
+
+import java.util.Collection;
+import java.util.List;
+
+import jline.Completor;
+import org.apache.geronimo.gshell.console.completer.StringsCompleter;
+import org.apache.servicemix.kernel.gshell.features.management.ManagedFeature;
+import org.apache.servicemix.kernel.gshell.features.management.ManagedFeaturesRegistry;
+
+/**
+ * {@link jline.Completor} for installed features.
+ *
+ * Displays a list of currently installed features.
+ *
+ */
+public class InstalledFeatureCompleter implements Completor {
+
+    private ManagedFeaturesRegistry featuresRegistry;
+    private StringsCompleter delegate;
+
+    public void setFeaturesRegistry(ManagedFeaturesRegistry featuresRegistry) {
+        this.featuresRegistry = featuresRegistry;
+    }
+
+    public int complete(final String buffer, final int cursor, final List candidates) {
+        Collection<ManagedFeature> features = featuresRegistry.getInstalledFeatures().values();
+        delegate = new StringsCompleter();
+
+        for (ManagedFeature feature : features) {
+            delegate.getStrings().add(feature.getName());
+        }
+
+        return delegate.complete(buffer, cursor, candidates);
+    }
+
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeatureDeploymentListener.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeatureDeploymentListener.java
new file mode 100644
index 0000000..e4a036b
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeatureDeploymentListener.java
@@ -0,0 +1,267 @@
+/*
+ * 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.servicemix.kernel.gshell.features.internal;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.ZipEntry;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.servicemix.kernel.filemonitor.DeploymentListener;
+import org.apache.servicemix.kernel.gshell.features.Feature;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.Constants;
+import org.osgi.framework.SynchronousBundleListener;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.osgi.context.BundleContextAware;
+
+/**
+ * A deployment listener able to hot deploy a feature descriptor
+ */
+public class FeatureDeploymentListener implements DeploymentListener, SynchronousBundleListener, BundleContextAware,
+                                                        InitializingBean, DisposableBean {
+
+    public static final String FEATURE_PATH = "org.apache.servicemix.kernel.gshell.features";
+
+    private static final Log LOGGER = LogFactory.getLog(FeatureDeploymentListener.class);
+
+    private DocumentBuilderFactory dbf;
+    private FeaturesServiceImpl featuresService;
+    private BundleContext bundleContext;
+
+    public void setFeaturesService(FeaturesServiceImpl featuresService) {
+        this.featuresService = featuresService;
+    }
+
+    public FeaturesServiceImpl getFeaturesService() {
+        return featuresService;
+    }
+
+    public BundleContext getBundleContext() {
+        return bundleContext;
+    }
+
+    public void setBundleContext(BundleContext bundleContext) {
+        this.bundleContext = bundleContext;
+    }
+
+    public void afterPropertiesSet() throws Exception {
+        bundleContext.addBundleListener(this);
+        for (Bundle bundle : bundleContext.getBundles()) {
+            bundleChanged(new BundleEvent(BundleEvent.INSTALLED, bundle));
+        }
+    }
+
+    public void destroy() throws Exception {
+        bundleContext.removeBundleListener(this);
+    }
+
+    public boolean canHandle(File artifact) {
+        try {
+            if (artifact.isFile() && artifact.getName().endsWith(".xml")) {
+                Document doc = parse(artifact);
+                String name = doc.getDocumentElement().getLocalName();
+                String uri  = doc.getDocumentElement().getNamespaceURI();
+                if ("features".equals(name) && (uri == null || "".equals(uri))) {
+                    return true;
+                }
+            }
+        } catch (Exception e) {
+            LOGGER.error("Unable to parse deployed file " + artifact.getAbsolutePath(), e);
+        }
+        return false;
+    }
+
+    public File handle(File artifact, File tmpDir) {
+        // We can't really install the feature right now and just return nothing.
+        // We would not be aware of the fact that the bundle has been uninstalled
+        // and therefore require the feature to be uninstalled.
+        // So instead, create a fake bundle with the file inside, which will be listened by
+        // this deployer: installation / uninstallation of the feature will be done
+        // while the bundle is installed / uninstalled.
+        try {
+            File destFile = new File(tmpDir, artifact.getName() + ".jar");
+            OutputStream os = new BufferedOutputStream(new FileOutputStream(destFile));
+
+            String name = artifact.getCanonicalPath();
+            int idx = name.lastIndexOf('/');
+            if (idx >= 0) {
+                name = name.substring(idx + 1);
+            }
+            String[] str = extractNameVersionType(name);
+            // Create manifest
+            Manifest m = new Manifest();
+            m.getMainAttributes().putValue("Manifest-Version", "2");
+            m.getMainAttributes().putValue(Constants.BUNDLE_MANIFESTVERSION, "2");
+            m.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, str[0]);
+            m.getMainAttributes().putValue(Constants.BUNDLE_VERSION, str[1]);
+            // Put content
+            JarOutputStream out = new JarOutputStream(os);
+            ZipEntry e = new ZipEntry(JarFile.MANIFEST_NAME);
+            out.putNextEntry(e);
+            m.write(out);
+            out.closeEntry();
+            e = new ZipEntry("META-INF/");
+            out.putNextEntry(e);
+            e = new ZipEntry("META-INF/" + FEATURE_PATH + "/");
+            out.putNextEntry(e);
+            out.closeEntry();
+            e = new ZipEntry("META-INF/" + FEATURE_PATH + "/" + name);
+            out.putNextEntry(e);
+            InputStream fis = new BufferedInputStream(new FileInputStream(artifact));
+            copyInputStream(fis, out);
+            fis.close();
+            out.closeEntry();
+            out.close();
+            os.close();
+            return destFile;
+        } catch (Exception e) {
+            LOGGER.error("Unable to build spring application bundle", e);
+            return null;
+        }
+    }
+
+    public void bundleChanged(BundleEvent bundleEvent) {
+        try {
+            Bundle bundle = bundleEvent.getBundle();
+            if (bundleEvent.getType() == BundleEvent.INSTALLED) {
+                Enumeration featuresUrlEnumeration = bundle.findEntries("/META-INF/" + FEATURE_PATH + "/", "*.xml", false);
+                while (featuresUrlEnumeration != null && featuresUrlEnumeration.hasMoreElements()) {
+                    URL url = (URL) featuresUrlEnumeration.nextElement();
+                    RepositoryImpl repo = featuresService.internalAddRepository(url.toURI());
+                    for (Feature f : repo.getFeatures()) {
+                        featuresService.installFeature(f.getName(), f.getVersion());
+                    }
+                    featuresService.internalRemoveRepository(url.toURI());
+                }
+            } else if (bundleEvent.getType() == BundleEvent.UNINSTALLED) {
+                Enumeration featuresUrlEnumeration = bundle.findEntries("/META-INF/" + FEATURE_PATH + "/", "*.xml", false);
+                while (featuresUrlEnumeration != null && featuresUrlEnumeration.hasMoreElements()) {
+                    URL url = (URL) featuresUrlEnumeration.nextElement();
+                    RepositoryImpl repo = featuresService.internalAddRepository(url.toURI());
+                    for (Feature f : repo.getFeatures()) {
+                        featuresService.uninstallFeature(f.getName(), f.getVersion());
+                    }
+                    featuresService.internalRemoveRepository(url.toURI());
+                }
+            }
+        } catch (Exception e) {
+            LOGGER.error("Unable to install / uninstall feature", e);
+        }
+    }
+
+    protected Document parse(File artifact) throws Exception {
+        if (dbf == null) {
+            dbf = DocumentBuilderFactory.newInstance();
+            dbf.setNamespaceAware(true);
+        }
+        DocumentBuilder db = dbf.newDocumentBuilder();
+        return db.parse(artifact);
+    }
+
+    private static void copyInputStream(InputStream in, OutputStream out) throws IOException {
+        byte[] buffer = new byte[8192];
+        int len = in.read(buffer);
+        while (len >= 0) {
+            out.write(buffer, 0, len);
+            len = in.read(buffer);
+        }
+    }
+    private static final String DEFAULT_VERSION = "0.0.0";
+
+    private static final Pattern ARTIFACT_MATCHER = Pattern.compile("(.+)(?:-(\\d+)(?:\\.(\\d+)(?:\\.(\\d+))?)?(?:[^a-zA-Z0-9](.*))?)(?:\\.([^\\.]+))", Pattern.DOTALL);
+    private static final Pattern FUZZY_MODIFIDER = Pattern.compile("(?:\\d+[.-])*(.*)", Pattern.DOTALL);
+
+    public static String[] extractNameVersionType(String url) {
+        Matcher m = ARTIFACT_MATCHER.matcher(url);
+        if (!m.matches()) {
+            return new String[] { url, DEFAULT_VERSION };
+        }
+        else {
+            //System.err.println(m.groupCount());
+            //for (int i = 1; i <= m.groupCount(); i++) {
+            //    System.err.println("Group " + i + ": " + m.group(i));
+            //}
+
+            StringBuffer v = new StringBuffer();
+            String d1 = m.group(1);
+            String d2 = m.group(2);
+            String d3 = m.group(3);
+            String d4 = m.group(4);
+            String d5 = m.group(5);
+            String d6 = m.group(6);
+            if (d2 != null) {
+                v.append(d2);
+                if (d3 != null) {
+                    v.append('.');
+                    v.append(d3);
+                    if (d4 != null) {
+                        v.append('.');
+                        v.append(d4);
+                        if (d5 != null) {
+                            v.append(".");
+                            cleanupModifier(v, d5);
+                        }
+                    } else if (d5 != null) {
+                        v.append(".0.");
+                        cleanupModifier(v, d5);
+                    }
+                } else if (d5 != null) {
+                    v.append(".0.0.");
+                    cleanupModifier(v, d5);
+                }
+            }
+            return new String[] { d1, v.toString(), d6 };
+        }
+    }
+
+    private static void cleanupModifier(StringBuffer result, String modifier) {
+        Matcher m = FUZZY_MODIFIDER.matcher(modifier);
+        if (m.matches()) {
+            modifier = m.group(1);
+        }
+        for (int i = 0; i < modifier.length(); i++) {
+            char c = modifier.charAt(i);
+            if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '-') {
+                result.append(c);
+            }
+        }
+    }
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeatureImpl.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeatureImpl.java
new file mode 100644
index 0000000..07806d3
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeatureImpl.java
@@ -0,0 +1,132 @@
+/*
+ * 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.servicemix.kernel.gshell.features.internal;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.servicemix.kernel.gshell.features.Feature;
+import org.springframework.jmx.export.annotation.ManagedAttribute;
+import org.springframework.jmx.export.annotation.ManagedResource;
+
+/**
+ * A feature
+ */
+@ManagedResource(description = "Managed Feature", currencyTimeLimit = 15)
+public class FeatureImpl implements Feature {
+
+    private String id;
+    private String name;
+    private String version;
+    private List<Feature> dependencies = new ArrayList<Feature>();
+    private List<String> bundles = new ArrayList<String>();
+    private Map<String, Map<String,String>> configs = new HashMap<String, Map<String,String>>();
+    public static String SPLIT_FOR_NAME_AND_VERSION = "_split_for_name_and_version_";
+    public static String DEFAULT_VERSION = "0.0.0";
+    
+    public FeatureImpl(String name) {
+        this(name, DEFAULT_VERSION);
+    }
+    
+    public FeatureImpl(String name, String version) {
+    	this.name = name;
+    	this.version = version;
+        this.id = name + "-" + version;
+    }
+
+    @ManagedAttribute(description = "Feature Unique ID")
+    public String getId() {
+        return id;
+    }
+
+    @ManagedAttribute(description = "Feature Name")
+    public String getName() {
+        return name;
+    }
+
+    @ManagedAttribute(description = "Feature Version")
+    public String getVersion() {
+		return version;
+	}
+    
+	public void setVersion(String version) {
+		this.version = version;
+	}
+
+    @ManagedAttribute(description = "List of Dependencies")
+    public List<Feature> getDependencies() {
+        return dependencies;
+    }
+
+    public List<String> getBundles() {
+        return bundles;
+    }
+
+    public Map<String, Map<String, String>> getConfigurations() {
+        return configs;
+    }
+
+    public void addDependency(Feature dependency) {
+        dependencies.add(dependency);
+    }
+
+    public void addBundle(String bundle) {
+        bundles.add(bundle);
+    }
+
+    public void addConfig(String name, Map<String,String> properties) {
+        configs.put(name, properties);
+    }
+
+    public String toString() {
+    	String ret = getName() + SPLIT_FOR_NAME_AND_VERSION + getVersion();
+    	return ret;
+    }
+    
+    public static Feature valueOf(String str) {
+    	if (str.indexOf(SPLIT_FOR_NAME_AND_VERSION) >= 0) {
+    		String strName = str.substring(0, str.indexOf(SPLIT_FOR_NAME_AND_VERSION));
+        	String strVersion = str.substring(str.indexOf(SPLIT_FOR_NAME_AND_VERSION) 
+        			+ SPLIT_FOR_NAME_AND_VERSION.length(), str.length());
+        	return new FeatureImpl(strName, strVersion);
+    	} else {
+    		return new FeatureImpl(str);
+    	}
+    			
+    	
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        FeatureImpl feature = (FeatureImpl) o;
+
+        if (!name.equals(feature.name)) return false;
+        if (!version.equals(feature.version)) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result = name.hashCode();
+        result = 31 * result + version.hashCode();
+        return result;
+    }
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeaturesServiceImpl.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeaturesServiceImpl.java
new file mode 100644
index 0000000..1c03515
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeaturesServiceImpl.java
@@ -0,0 +1,599 @@
+/*
+ * 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.servicemix.kernel.gshell.features.internal;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.servicemix.kernel.gshell.features.Feature;
+import org.apache.servicemix.kernel.gshell.features.FeaturesRegistry;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+import org.apache.servicemix.kernel.gshell.features.Repository;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.Version;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.prefs.BackingStoreException;
+import org.osgi.service.prefs.Preferences;
+import org.osgi.service.prefs.PreferencesService;
+import org.springframework.osgi.context.BundleContextAware;
+
+/**
+ * The Features service implementation.
+ * Adding a repository url will load the features contained in this repository and
+ * create dummy sub shells.  When invoked, these commands will prompt the user for
+ * installing the needed bundles.
+ *
+ */
+public class FeaturesServiceImpl implements FeaturesService, BundleContextAware {
+
+    private static final String ALIAS_KEY = "_alias_factory_pid";
+
+    private static final Log LOGGER = LogFactory.getLog(FeaturesServiceImpl.class);
+
+    private BundleContext bundleContext;
+    private ConfigurationAdmin configAdmin;
+    private PreferencesService preferences;
+    private Set<URI> uris;
+    private Map<URI, RepositoryImpl> repositories = new HashMap<URI, RepositoryImpl>();
+    private Map<String, Map<String, Feature>> features;
+    private Map<Feature, Set<Long>> installed = new HashMap<Feature, Set<Long>>();
+    private String boot;
+    private boolean bootFeaturesInstalled;
+    private FeaturesRegistry featuresRegistry;
+
+    public BundleContext getBundleContext() {
+        return bundleContext;
+    }
+
+    public void setBundleContext(BundleContext bundleContext) {
+        this.bundleContext = bundleContext;
+    }
+
+    public ConfigurationAdmin getConfigAdmin() {
+        return configAdmin;
+    }
+
+    public void setConfigAdmin(ConfigurationAdmin configAdmin) {
+        this.configAdmin = configAdmin;
+    }
+
+    public PreferencesService getPreferences() {
+        return preferences;
+    }
+
+    public void setPreferences(PreferencesService preferences) {
+        this.preferences = preferences;
+    }
+
+    public void setFeaturesServiceRegistry(FeaturesRegistry featuresRegistry) {
+        this.featuresRegistry = featuresRegistry;
+    }
+
+    public void setUrls(String uris) throws URISyntaxException {
+        String[] s = uris.split(",");
+        this.uris = new HashSet<URI>();
+        for (int i = 0; i < s.length; i++) {
+            this.uris.add(new URI(s[i]));
+        }
+    }
+
+    public void setBoot(String boot) {
+        this.boot = boot;
+    }
+
+    public void addRepository(URI uri) throws Exception {
+        if (!repositories.values().contains(uri)) {
+            internalAddRepository(uri);
+            saveState();
+        }
+    }
+
+    protected RepositoryImpl internalAddRepository(URI uri) throws Exception {
+        RepositoryImpl repo = new RepositoryImpl(uri);
+        repositories.put(uri, repo);
+        featuresRegistry.register(repo);
+        features = null;
+        return repo;
+    }
+
+    public void removeRepository(URI uri) {
+        if (repositories.containsKey(uri)) {
+            internalRemoveRepository(uri);
+            saveState();
+        }
+    }
+
+    public void internalRemoveRepository(URI uri) {
+        featuresRegistry.unregister(repositories.get(uri));
+        repositories.remove(uri);
+        features = null;
+    }
+
+    public Repository[] listRepositories() {
+        Collection<RepositoryImpl> repos = repositories.values();
+        return repos.toArray(new Repository[repos.size()]);
+    }
+
+    public void installAllFeatures(URI uri) throws Exception {
+        RepositoryImpl repo = internalAddRepository(uri);
+        for (Feature f : repo.getFeatures()) {
+            installFeature(f.getName(), f.getVersion());
+        }
+        internalRemoveRepository(uri);            
+    }
+
+    public void uninstallAllFeatures(URI uri) throws Exception {
+        RepositoryImpl repo = internalAddRepository(uri);
+        for (Feature f : repo.getFeatures()) {
+            uninstallFeature(f.getName(), f.getVersion());
+        }
+        internalRemoveRepository(uri);            
+    }
+
+    public void installFeature(String name) throws Exception {
+    	installFeature(name, FeatureImpl.DEFAULT_VERSION);
+    }
+
+    public void installFeature(String name, String version) throws Exception {
+        Feature f = getFeature(name, version);
+        if (f == null) {
+            throw new Exception("No feature named '" + name 
+            		+ "' with version '" + version + "' available");
+        }
+        for (Feature dependency : f.getDependencies()) {
+        	installFeature(dependency.getName(), dependency.getVersion());
+        }
+        for (String config : f.getConfigurations().keySet()) {
+            Dictionary<String,String> props = new Hashtable<String, String>(f.getConfigurations().get(config));
+            String[] pid = parsePid(config);
+            if (pid[1] != null) {
+                props.put(ALIAS_KEY, pid[1]);
+            }
+            Configuration cfg = getConfiguration(configAdmin, pid[0], pid[1]);
+            if (cfg.getBundleLocation() != null) {
+                cfg.setBundleLocation(null);
+            }
+            cfg.update(props);
+        }
+        Set<Long> bundles = new HashSet<Long>();
+        for (String bundleLocation : f.getBundles()) {
+            Bundle b = installBundleIfNeeded(bundleLocation);
+            bundles.add(b.getBundleId());
+        }
+        for (long id : bundles) {
+            bundleContext.getBundle(id).start();
+        }
+
+        featuresRegistry.registerInstalled(f);
+        installed.put(f, bundles);
+        saveState();
+    }
+    protected Bundle installBundleIfNeeded(String bundleLocation) throws IOException, BundleException {
+        LOGGER.debug("Checking " + bundleLocation);
+        InputStream is = null;
+        try {
+            is = new BufferedInputStream(new URL(bundleLocation).openStream());
+        } catch (RuntimeException e) {
+            LOGGER.error(e.getMessage());
+            throw e;
+        }
+        try {
+            is.mark(256 * 1024);
+            JarInputStream jar = new JarInputStream(is);
+            Manifest m = jar.getManifest();
+            String sn = m.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
+            String vStr = m.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
+            Version v = vStr == null ? Version.emptyVersion : Version.parseVersion(vStr);
+            for (Bundle b : bundleContext.getBundles()) {
+                if (b.getSymbolicName() != null && b.getSymbolicName().equals(sn)) {
+                    vStr = (String) b.getHeaders().get(Constants.BUNDLE_VERSION);
+                    Version bv = vStr == null ? Version.emptyVersion : Version.parseVersion(vStr);
+                    if (v.equals(bv)) {
+                        LOGGER.debug("  found installed bundle: " + b);
+                        return b;
+                    }
+                }
+            }
+            try {
+                is.reset();
+            } catch (IOException e) {
+                is.close();
+                is = new BufferedInputStream(new URL(bundleLocation).openStream());
+            }
+            LOGGER.debug("Installing bundle " + bundleLocation);
+            return getBundleContext().installBundle(bundleLocation, is);
+        } finally {
+            is.close();
+        }
+    }
+
+    public void uninstallFeature(String name) throws Exception {
+        List<String> versions = new ArrayList<String>();
+        for (Feature f : installed.keySet()) {
+            if (name.equals(f.getName())) {
+                versions.add(f.getVersion());
+            }
+        }
+        if (versions.size() == 0) {
+            throw new Exception("Feature named '" + name + "' is not installed");
+        } else if (versions.size() > 1) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("Feature named '" + name + "' has multiple versions installed (");
+            for (int i = 0; i < versions.size(); i++) {
+                if (i > 0) {
+                    sb.append(", ");
+                }
+                sb.append(versions.get(i));
+            }
+            sb.append("). Please specify the version to uninstall.");
+            throw new Exception(sb.toString());
+        }
+        uninstallFeature(name, versions.get(0));
+    }
+    
+    public void uninstallFeature(String name, String version) throws Exception {
+    	Feature feature = getFeature(name, version);
+        if (feature == null || !installed.containsKey(feature)) {
+            throw new Exception("Feature named '" + name 
+            		+ "' with version '" + version + "' is not installed");
+        }
+        // Grab all the bundles installed by this feature
+        // and remove all those who will still be in use.
+        // This gives this list of bundles to uninstall.
+        Set<Long> bundles = installed.remove(feature);
+        for (Set<Long> b : installed.values()) {
+            bundles.removeAll(b);
+        }
+        for (long bundleId : bundles) {
+            getBundleContext().getBundle(bundleId).uninstall();
+        }
+        featuresRegistry.unregisterInstalled(feature);
+        saveState();
+    }
+
+    public String[] listFeatures() throws Exception {
+        Collection<String> features = new ArrayList<String>();
+        for (Map<String, Feature> featureWithDifferentVersion : getFeatures()
+				.values()) {
+			for (Feature f : featureWithDifferentVersion.values()) {
+				String installStatus = installed.containsKey(f) ? "installed  "
+						: "uninstalled";
+				String version = f.getVersion();
+				switch (version.length()) {
+				case 1:
+					version = "       " + version;
+				case 2:
+					version = "      " + version;
+				case 3:
+					version = "     " + version;
+				case 4:
+					version = "    " + version;
+				case 5:
+					version = "   " + version;
+				case 6:
+					version = "  " + version;
+				case 7:
+					version = " " + version;
+				}
+				features.add("[" + installStatus + "] " + " [" + version + "] "
+						+ f.getName());
+			}
+		}
+        return features.toArray(new String[features.size()]);
+    }
+
+    public String[] listInstalledFeatures() {
+        List<String> result = new ArrayList<String>();
+        for (Feature feature : installed.keySet()) {
+            result.add(feature.getName());
+        }
+        return result.toArray(new String[result.size()]);
+    }
+
+    protected Feature getFeature(String name, String version) throws Exception {
+        Map<String, Feature> versions = getFeatures().get(name);
+        if (versions == null || versions.isEmpty()) {
+            return null;
+        } else {
+            Feature feature = versions.get(version);
+            if (feature == null && FeatureImpl.DEFAULT_VERSION.equals(version)) {
+                Version latest = new Version(cleanupVersion(version));
+                for (String available : versions.keySet()) {
+                    Version availableVersion = new Version(cleanupVersion(available));
+                    if (availableVersion.compareTo(latest) > 0) {
+                        feature = versions.get(available);
+                        latest = availableVersion;
+                    }
+                }
+            }
+            return feature;
+        }
+    }
+
+    protected Map<String, Map<String, Feature>> getFeatures() throws Exception {
+        if (features == null) {
+        	//the outer map's key is feature name, the inner map's key is feature version       
+            Map<String, Map<String, Feature>> map = new HashMap<String, Map<String, Feature>>();
+            // Two phase load:
+            // * first load dependent repositories
+            for (;;) {
+                boolean newRepo = false;
+                for (Repository repo : listRepositories()) {
+                    for (URI uri : repo.getRepositories()) {
+                        if (!repositories.keySet().contains(uri)) {
+                            internalAddRepository(uri);
+                            newRepo = true;
+                        }
+                    }
+                }
+                if (!newRepo) {
+                    break;
+                }
+            }
+            // * then load all features
+            for (Repository repo : repositories.values()) {
+                for (Feature f : repo.getFeatures()) {
+                	if (map.get(f.getName()) == null) {
+                		Map<String, Feature> versionMap = new HashMap<String, Feature>();
+                		versionMap.put(f.getVersion(), f);
+                		map.put(f.getName(), versionMap);
+                	} else {
+                		map.get(f.getName()).put(f.getVersion(), f);
+                	}
+                }
+            }
+            features = map;
+        }
+        return features;
+    }
+
+    public void start() throws Exception {
+        if (!loadState()) {
+            if (uris != null) {
+                for (URI uri : uris) {
+                    internalAddRepository(uri);
+                }
+            }
+            saveState();
+        }
+        if (boot != null && !bootFeaturesInstalled) {
+            new Thread() {
+                public void run() {
+                    String[] list = boot.split(",");
+                    for (String f : list) {
+                        if (f.length() > 0) {
+                            try {
+                                installFeature(f);
+                            } catch (Exception e) {
+                                LOGGER.error("Error installing boot feature " + f, e);
+                            }
+                        }
+                    }
+                    bootFeaturesInstalled = true;
+                    saveState();
+                }
+            }.start();
+        }
+    }
+
+    public void stop() throws Exception {
+        uris = new HashSet<URI>(repositories.keySet());
+        while (!repositories.isEmpty()) {
+            internalRemoveRepository(repositories.keySet().iterator().next());
+        }
+    }
+
+    protected String[] parsePid(String pid) {
+        int n = pid.indexOf('-');
+        if (n > 0) {
+            String factoryPid = pid.substring(n + 1);
+            pid = pid.substring(0, n);
+            return new String[]{pid, factoryPid};
+        } else {
+            return new String[]{pid, null};
+        }
+    }
+
+    protected Configuration getConfiguration(ConfigurationAdmin configurationAdmin,
+                                             String pid, String factoryPid) throws IOException, InvalidSyntaxException {
+        if (factoryPid != null) {
+            Configuration[] configs = configurationAdmin.listConfigurations("(|(" + ALIAS_KEY + "=" + pid + ")(.alias_factory_pid=" + factoryPid + "))");
+            if (configs == null || configs.length == 0) {
+                return configurationAdmin.createFactoryConfiguration(pid, null);
+            } else {
+                return configs[0];
+            }
+        } else {
+            return configurationAdmin.getConfiguration(pid, null);
+        }
+    }
+    
+    protected void saveState() {
+        try {
+            Preferences prefs = preferences.getUserPreferences("FeaturesServiceState");
+            saveSet(prefs.node("repositories"), repositories.keySet());
+            saveMap(prefs.node("features"), installed);
+            prefs.putBoolean("bootFeaturesInstalled", bootFeaturesInstalled);
+            prefs.flush();
+        } catch (Exception e) {
+            LOGGER.error("Error persisting FeaturesService state", e);
+        }
+    }
+
+    protected boolean loadState() {
+        try {
+            Preferences prefs = preferences.getUserPreferences("FeaturesServiceState");
+            if (prefs.nodeExists("repositories")) {
+                Set<URI> repositories = loadSet(prefs.node("repositories"));
+                for (URI repo : repositories) {
+                    internalAddRepository(repo);
+                }
+                installed = loadMap(prefs.node("features"));
+                for (Feature f : installed.keySet()) {
+                    featuresRegistry.registerInstalled(f);
+                }
+                bootFeaturesInstalled = prefs.getBoolean("bootFeaturesInstalled", false);
+                return true;
+            }
+        } catch (Exception e) {
+            LOGGER.error("Error loading FeaturesService state", e);
+        }
+        return false;
+    }
+
+    protected void saveSet(Preferences node, Set<URI> set) throws BackingStoreException {
+        List<URI> l = new ArrayList<URI>(set);
+        node.clear();
+        node.putInt("count", l.size());
+        for (int i = 0; i < l.size(); i++) {
+            node.put("item." + i, l.get(i).toString());
+        }
+    }
+
+    protected Set<URI> loadSet(Preferences node) {
+        Set<URI> l = new HashSet<URI>();
+        int count = node.getInt("count", 0);
+        for (int i = 0; i < count; i++) {
+            l.add(URI.create(node.get("item." + i, null)));
+        }
+        return l;
+    }
+
+    protected void saveMap(Preferences node, Map<Feature, Set<Long>> map) throws BackingStoreException {
+        node.clear();
+        for (Map.Entry<Feature, Set<Long>> entry : map.entrySet()) {
+            Feature key = entry.getKey();
+            String val = createValue(entry.getValue());
+            node.put(key.toString(), val);
+        }
+    }
+
+    protected Map<Feature, Set<Long>> loadMap(Preferences node) throws BackingStoreException {
+        Map<Feature, Set<Long>> map = new HashMap<Feature, Set<Long>>();
+        for (String key : node.keys()) {
+            String val = node.get(key, null);
+            Set<Long> set = readValue(val);
+            map.put(FeatureImpl.valueOf(key), set);
+        }
+        return map;
+    }
+
+    protected String createValue(Set<Long> set) {
+        StringBuilder sb = new StringBuilder();
+        for (long i : set) {
+            if (sb.length() > 0) {
+                sb.append(",");
+            }
+            sb.append(i);
+        }
+        return sb.toString();
+    }
+
+    protected Set<Long> readValue(String val) {
+        Set<Long> set = new HashSet<Long>();
+        for (String str : val.split(",")) {
+            set.add(Long.parseLong(str));
+        }
+        return set;
+    }
+
+    /**
+     * Clean up version parameters. Other builders use more fuzzy definitions of
+     * the version syntax. This method cleans up such a version to match an OSGi
+     * version.
+     *
+     * @param version
+     * @return
+     */
+    static Pattern fuzzyVersion  = Pattern.compile("(\\d+)(\\.(\\d+)(\\.(\\d+))?)?([^a-zA-Z0-9](.*))?",
+                                                   Pattern.DOTALL);
+    static Pattern fuzzyModifier = Pattern.compile("(\\d+[.-])*(.*)",
+                                                   Pattern.DOTALL);
+
+    static public String cleanupVersion(String version) {
+        Matcher m = fuzzyVersion.matcher(version);
+        if (m.matches()) {
+            StringBuffer result = new StringBuffer();
+            String d1 = m.group(1);
+            String d2 = m.group(3);
+            String d3 = m.group(5);
+            String qualifier = m.group(7);
+
+            if (d1 != null) {
+                result.append(d1);
+                if (d2 != null) {
+                    result.append(".");
+                    result.append(d2);
+                    if (d3 != null) {
+                        result.append(".");
+                        result.append(d3);
+                        if (qualifier != null) {
+                            result.append(".");
+                            cleanupModifier(result, qualifier);
+                        }
+                    } else if (qualifier != null) {
+                        result.append(".0.");
+                        cleanupModifier(result, qualifier);
+                    }
+                } else if (qualifier != null) {
+                    result.append(".0.0.");
+                    cleanupModifier(result, qualifier);
+                }
+                return result.toString();
+            }
+        }
+        return version;
+    }
+
+    static void cleanupModifier(StringBuffer result, String modifier) {
+        Matcher m = fuzzyModifier.matcher(modifier);
+        if (m.matches())
+            modifier = m.group(2);
+
+        for (int i = 0; i < modifier.length(); i++) {
+            char c = modifier.charAt(i);
+            if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z')
+                    || (c >= 'A' && c <= 'Z') || c == '_' || c == '-')
+                result.append(c);
+        }
+    }
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/RepositoryImpl.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/RepositoryImpl.java
new file mode 100644
index 0000000..485c3d4
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/RepositoryImpl.java
@@ -0,0 +1,168 @@
+/*
+ * 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.servicemix.kernel.gshell.features.internal;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.apache.servicemix.kernel.gshell.features.Feature;
+import org.apache.servicemix.kernel.gshell.features.Repository;
+import org.springframework.jmx.export.annotation.ManagedAttribute;
+import org.springframework.jmx.export.annotation.ManagedOperation;
+import org.springframework.jmx.export.annotation.ManagedResource;
+import org.xml.sax.SAXException;
+
+/**
+ * The repository implementation.
+ */
+@ManagedResource
+public class RepositoryImpl implements Repository {
+
+    private URI uri;
+    private List<Feature> features;
+    private List<URI> repositories;
+
+    public RepositoryImpl(URI uri) {
+        this.uri = uri;
+    }
+
+    @ManagedAttribute
+    public URI getURI() {
+        return uri;
+    }
+
+    @ManagedOperation
+    public URI[] getRepositories() throws Exception {
+        if (repositories == null) {
+            load();
+        }
+        return repositories.toArray(new URI[repositories.size()]);
+    }
+
+    @ManagedOperation(description = "List of Features provided by this repository")
+    public Feature[] getFeatures() throws Exception {
+        if (features == null) {
+            load();
+        }
+        return features.toArray(new Feature[features.size()]);
+    }
+
+    public void load() throws IOException {
+        try {
+            repositories = new ArrayList<URI>();
+            features = new ArrayList<Feature>();
+            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+            Document doc = factory.newDocumentBuilder().parse(uri.toURL().openStream());
+            NodeList nodes = doc.getDocumentElement().getChildNodes();
+            for (int i = 0; i < nodes.getLength(); i++) {
+                Node node = nodes.item(i);
+                if (!(node instanceof Element)) {
+                    continue;
+                }
+                if ("repository".equals(node.getNodeName())) {
+                    Element e = (Element) nodes.item(i);
+                    repositories.add(new URI(e.getTextContent()));
+                } else if ("feature".equals(node.getNodeName())) {
+                    Element e = (Element) nodes.item(i);
+                    String name = e.getAttribute("name");
+                    String version = e.getAttribute("version");
+                    FeatureImpl f;
+                    if (version != null && version.length() > 0) {
+                        f = new FeatureImpl(name, version);
+                    } else {
+                        f = new FeatureImpl(name);
+                    }
+
+                    NodeList featureNodes = e.getElementsByTagName("feature");
+                    for (int j = 0; j < featureNodes.getLength(); j++) {
+                        Element b = (Element) featureNodes.item(j);
+                        String dependencyFeatureVersion = b.getAttribute("version");
+                        if (dependencyFeatureVersion != null && dependencyFeatureVersion.length() > 0) {
+                        	f.addDependency(new FeatureImpl(b.getTextContent(), dependencyFeatureVersion));
+                        } else {
+                        	f.addDependency(new FeatureImpl(b.getTextContent()));
+                        }
+                    }
+                    NodeList configNodes = e.getElementsByTagName("config");
+                    for (int j = 0; j < configNodes.getLength(); j++) {
+                        Element c = (Element) configNodes.item(j);
+                        String cfgName = c.getAttribute("name");
+                        String data = c.getTextContent();
+                        Properties properties = new Properties();
+                        properties.load(new ByteArrayInputStream(data.getBytes()));
+                        interpolation(properties);
+                        Map<String, String> hashtable = new Hashtable<String, String>();
+                        for (Object key : properties.keySet()) {
+                            String n = key.toString();
+                            hashtable.put(n, properties.getProperty(n));
+                        }
+                        f.addConfig(cfgName, hashtable);
+                    }
+                    NodeList bundleNodes = e.getElementsByTagName("bundle");
+                    for (int j = 0; j < bundleNodes.getLength(); j++) {
+                        Element b = (Element) bundleNodes.item(j);
+                        f.addBundle(b.getTextContent());
+                    }
+                    features.add(f);
+                }
+            }
+        } catch (SAXException e) {
+            throw (IOException) new IOException().initCause(e);
+        } catch (ParserConfigurationException e) {
+            throw (IOException) new IOException().initCause(e);
+        } catch (URISyntaxException e) {
+            throw (IOException) new IOException(e.getMessage() + " : " + uri).initCause(e);
+        } catch (IllegalArgumentException e) {
+            throw (IOException) new IOException(e.getMessage() + " : " + uri).initCause(e);
+        }
+    }
+
+    protected void interpolation(Properties properties) {
+        for (Enumeration e = properties.propertyNames(); e.hasMoreElements();) {
+            String key = (String)e.nextElement();
+            String val = properties.getProperty(key);
+            Matcher matcher = Pattern.compile("\\$\\{([^}]+)\\}").matcher(val);
+            while (matcher.find()) {
+                String rep = System.getProperty(matcher.group(1));
+                if (rep != null) {
+                    val = val.replace(matcher.group(0), rep);
+                    matcher.reset(val);
+                }
+            }
+            properties.put(key, val);
+        }
+    }
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/DefaultNamingStrategy.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/DefaultNamingStrategy.java
new file mode 100644
index 0000000..6bee5fc
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/DefaultNamingStrategy.java
@@ -0,0 +1,84 @@
+/*
+ * 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.servicemix.kernel.gshell.features.management;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.apache.servicemix.kernel.gshell.features.FeaturesRegistry;
+
+/**
+ * Naming strategy for JMX MBeans.
+ */
+public class DefaultNamingStrategy implements NamingStrategy {
+
+    private String jmxDomainName;
+
+    public String getJmxDomainName() {
+        return jmxDomainName;
+    }
+
+    public void setJmxDomainName(String jmxDomainName) {
+        this.jmxDomainName = jmxDomainName;
+    }
+
+    public ObjectName getObjectName(ManagedFeature feature) throws MalformedObjectNameException {
+        return getObjectName(feature, false);
+    }
+
+    public ObjectName getObjectName(ManagedFeature feature, boolean installed) throws MalformedObjectNameException {
+        StringBuffer sb = new StringBuffer();
+        sb.append(jmxDomainName).append(":Service=Features,");
+
+        if (installed) {
+            sb.append("Type=Installed,");
+        } else {
+            sb.append("Type=Available,");
+        }
+
+        sb.append("Name=").append(sanitize(feature.getName())).append(",")
+          .append("FeatureVersion=").append(sanitize(feature.getVersion()));
+
+        return new ObjectName(sb.toString());
+    }
+
+    public ObjectName getObjectName(ManagedRepository repository) throws MalformedObjectNameException {
+        return new ObjectName(jmxDomainName + ":" +
+                                    "Service=Features," +
+                                    "Type=Repositories," +
+                                    "Name=" + sanitize(repository.getUri().toString())); // + "," +
+    }
+
+    public ObjectName getObjectName(FeaturesRegistry featuresRegistry) throws MalformedObjectNameException {
+        return new ObjectName(jmxDomainName + ":" +
+                                    "Service=Features," +
+                                    "Name=FeaturesService");
+    }
+
+    private String sanitize(String in) {
+        String result = null;
+        if (in != null) {
+            result = in.replace(':', '_');
+            result = result.replace('/', '_');
+            result = result.replace('\\', '_');
+            result = result.replace('?', '_');
+            result = result.replace('=', '_');
+            result = result.replace(',', '_');
+        }
+        return result;
+    }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedFeature.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedFeature.java
new file mode 100644
index 0000000..1da589b
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedFeature.java
@@ -0,0 +1,78 @@
+/*
+ * 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.servicemix.kernel.gshell.features.management;
+
+import java.util.List;
+
+import org.apache.servicemix.kernel.gshell.features.Feature;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+import org.springframework.jmx.export.annotation.ManagedAttribute;
+import org.springframework.jmx.export.annotation.ManagedOperation;
+import org.springframework.jmx.export.annotation.ManagedResource;
+
+/**
+ * Managed Repository MBean
+ */
+@ManagedResource(description = "Feature")
+public class ManagedFeature {
+    private Feature feature;
+    private FeaturesService featuresService;
+    private String id;
+
+    public ManagedFeature(Feature feature, FeaturesService featuresService) {
+        this.feature = feature;
+        id = feature.getName() + "-" + feature.getVersion();
+        this.featuresService = featuresService;
+    }
+
+    @ManagedAttribute
+    public String getId() {
+        return id;    
+    }
+
+    @ManagedAttribute
+    public String getName() {
+        return feature.getName();
+    }
+
+    @ManagedAttribute
+    public String getVersion() {
+        return feature.getVersion();
+    }
+
+    @ManagedAttribute
+    public List<Feature> getDependencies() {
+        return feature.getDependencies();
+    }
+
+    @ManagedAttribute
+    public List<String> getBundles() {
+        return feature.getBundles();
+    }
+
+    @ManagedOperation
+    public void uninstallFeature() throws Exception {
+        featuresService.uninstallFeature(feature.getName(), feature.getVersion());
+    }
+
+    @ManagedOperation
+    public void installFeature() throws Exception {
+        featuresService.installFeature(feature.getName(), feature.getVersion());
+    }
+
+}
+
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedFeaturesRegistry.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedFeaturesRegistry.java
new file mode 100644
index 0000000..9fbdb46
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedFeaturesRegistry.java
@@ -0,0 +1,210 @@
+/*
+ * 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.servicemix.kernel.gshell.features.management;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.management.MBeanServer;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.servicemix.kernel.gshell.features.Feature;
+import org.apache.servicemix.kernel.gshell.features.FeaturesRegistry;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+import org.apache.servicemix.kernel.gshell.features.Repository;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.jmx.export.annotation.ManagedOperation;
+import org.springframework.jmx.export.annotation.ManagedResource;
+
+/**
+ * The FeaturesServiceRegistry maintains the managed Features and Repositories
+ * for JMX management.
+ */
+@ManagedResource(description = "Features Service Registry and Management")
+public class ManagedFeaturesRegistry implements InitializingBean, FeaturesRegistry {
+
+    private static final transient Log LOG = LogFactory.getLog(ManagedFeaturesRegistry.class);
+
+    private NamingStrategy namingStrategy;
+    private ManagementAgent managementAgent;
+    private Map<String, ManagedFeature> availableFeatures;
+    private Map<String, ManagedFeature> installedFeatures;
+    private Map<String, ManagedRepository> repositories;
+    private boolean mbeanServerInitialized;
+    private FeaturesService featuresService;
+
+    @ManagedOperation
+    public void installFeature(String name) throws Exception {
+        featuresService.installFeature(name);
+    }
+
+    @ManagedOperation
+    public void installFeature(String name, String version) throws Exception {
+        featuresService.installFeature(name, version);
+    }
+
+    @ManagedOperation
+    public void installRepository(String repositoryUri) throws Exception {
+        featuresService.addRepository(new URI(repositoryUri));
+    }
+
+    public ManagedFeaturesRegistry() {
+        availableFeatures = new ConcurrentHashMap<String, ManagedFeature>();
+        installedFeatures = new ConcurrentHashMap<String, ManagedFeature>();
+        repositories = new ConcurrentHashMap<String, ManagedRepository>();
+    }
+
+    public Map<String, ManagedFeature> getAvailableFeatures() {
+        return availableFeatures;
+    }
+
+    public Map<String, ManagedFeature> getInstalledFeatures() {
+        return installedFeatures;
+    }
+
+    public Map<String, ManagedRepository> getRepositories() {
+        return repositories;
+    }
+
+    public void setFeaturesService(FeaturesService featuresService) {
+        this.featuresService = featuresService;
+    }
+
+    public void setNamingStrategy(NamingStrategy namingStrategy) {
+        this.namingStrategy = namingStrategy;
+    }
+
+    public void setManagementAgent(ManagementAgent managementAgent) {
+        this.managementAgent = managementAgent;
+    }
+
+    public void register(Feature feature) {
+        try {
+            ManagedFeature mf = new ManagedFeature(feature, featuresService);
+            availableFeatures.put(feature.getId(), mf);
+            if ( mbeanServerInitialized ) {
+                managementAgent.register(mf, namingStrategy.getObjectName(mf));
+            }
+        } catch (Exception e) {
+            LOG.warn("Unable to register managed feature: " + e, e);
+        }
+    }
+
+    public void unregister(Feature feature) {
+        try {
+            ManagedFeature mf = availableFeatures.remove(feature.getId());
+            if ( mbeanServerInitialized ) {
+                managementAgent.unregister(namingStrategy.getObjectName(mf));
+            }
+        } catch (Exception e) {
+            LOG.warn("Unable to unregister managed feature: " + e, e);
+        }
+    }
+
+    public void registerInstalled(Feature feature) {
+        try {
+            ManagedFeature mf = new ManagedFeature(feature, featuresService);
+            installedFeatures.put(feature.getId(), mf);
+            if ( mbeanServerInitialized ) {
+                managementAgent.register(mf, namingStrategy.getObjectName(mf, true));
+            }
+        } catch (Exception e) {
+            LOG.warn("Unable to register managed feature: " + e, e);
+        }
+    }
+
+    public void unregisterInstalled(Feature feature) {
+        try {
+            ManagedFeature mf = installedFeatures.remove(feature.getId());
+            if ( mbeanServerInitialized ) {
+                managementAgent.unregister(namingStrategy.getObjectName(mf, true));
+            }
+        } catch (Exception e) {
+            LOG.warn("Unable to unregister managed feature: " + e, e);
+        }
+    }
+
+    public void register(Repository repository) {
+        try {
+            ManagedRepository mr = new ManagedRepository(repository, featuresService);
+            repositories.put(repository.getURI().toString(), mr);
+
+            for (Feature f : repository.getFeatures()) {
+                // TODO: Associate the feature with the Repo?
+                register(f);
+            }
+
+            if ( mbeanServerInitialized ) {
+                managementAgent.register(mr, namingStrategy.getObjectName(mr));
+            }
+        } catch (Exception e) {
+            LOG.warn("Unable to register managed repository: " + e, e);
+        }
+    }
+
+    public void unregister(Repository repository) {
+        try {
+            ManagedRepository mr = repositories.remove(repository.getURI().toString());
+
+            for (Feature f : repository.getFeatures()) {
+                // TODO: Associate the feature with the Repo?
+                unregister(f);
+            }
+
+            if ( mbeanServerInitialized ) {
+                managementAgent.unregister(namingStrategy.getObjectName(mr));
+            }
+        } catch (Exception e) {
+            LOG.warn("Unable to unregister managed repository: " + e, e);
+        }
+    }
+
+    public void afterPropertiesSet() throws Exception {
+        if (managementAgent == null) {
+            throw new IllegalArgumentException("managementAgent must not be null");
+        }
+        if (namingStrategy == null) {
+            throw new IllegalArgumentException("namingStrategy must not be null");
+        }
+    }
+
+    public void registerMBeanServer(MBeanServer mbeanServer, Map props ) throws Exception {
+        if (mbeanServer != null) {
+            mbeanServerInitialized = true;
+        }
+
+        managementAgent.register(this, namingStrategy.getObjectName(this));
+
+        for (ManagedRepository repository : repositories.values()) {
+            managementAgent.register(repository, namingStrategy.getObjectName(repository));
+        }
+
+        for (ManagedFeature feature : availableFeatures.values()) {
+            managementAgent.register(feature, namingStrategy.getObjectName(feature));
+        }
+
+        for (ManagedFeature feature : installedFeatures.values()) {
+            installedFeatures.put(feature.getId(), feature);
+            managementAgent.register(feature, namingStrategy.getObjectName(feature, true));
+        }
+
+    }
+
+    
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedRepository.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedRepository.java
new file mode 100644
index 0000000..2d3ae17
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedRepository.java
@@ -0,0 +1,57 @@
+/*
+ * 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.servicemix.kernel.gshell.features.management;
+
+import java.net.URI;
+
+import org.apache.servicemix.kernel.gshell.features.Feature;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+import org.apache.servicemix.kernel.gshell.features.Repository;
+import org.springframework.jmx.export.annotation.ManagedAttribute;
+import org.springframework.jmx.export.annotation.ManagedOperation;
+import org.springframework.jmx.export.annotation.ManagedResource;
+
+@ManagedResource(description = "Features Repository")
+public class ManagedRepository {
+    private Repository repository;
+    private FeaturesService featuresService;
+
+    public ManagedRepository(Repository repository, FeaturesService featuresService) {
+        this.repository = repository;
+        this.featuresService = featuresService;
+    }
+
+    @ManagedAttribute
+    public URI getUri() {
+        return repository.getURI();
+    }
+
+    @ManagedAttribute
+    public URI[] getRepositories() throws Exception {
+        return repository.getRepositories();
+    }
+
+    @ManagedAttribute
+    public Feature[] getFeatures() throws Exception {
+        return repository.getFeatures();
+    }
+
+    @ManagedOperation
+    public void removeRepository() throws Exception {
+        featuresService.removeRepository(repository.getURI());
+    }
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagementAgent.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagementAgent.java
new file mode 100644
index 0000000..611ec6b
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagementAgent.java
@@ -0,0 +1,130 @@
+/*
+ * 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.servicemix.kernel.gshell.features.management;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.JMException;
+import javax.management.MBeanServer;
+import javax.management.NotCompliantMBeanException;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.modelmbean.InvalidTargetObjectTypeException;
+import javax.management.modelmbean.ModelMBeanInfo;
+import javax.management.modelmbean.RequiredModelMBean;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource;
+import org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler;
+
+/**
+ * Management Agent that registers MBeans with JMX MBeanServer.
+ */
+public class ManagementAgent implements DisposableBean {
+
+    private static final transient Log LOG = LogFactory.getLog(ManagementAgent.class);
+
+    private MBeanServer mbeanServer;
+    private MetadataMBeanInfoAssembler assembler;
+    private Set<ObjectName> mbeans = new HashSet<ObjectName>();
+
+    public ManagementAgent() {
+        assembler = new MetadataMBeanInfoAssembler();
+        assembler.setAttributeSource(new AnnotationJmxAttributeSource());
+    }
+
+    public MBeanServer getMbeanServer() {
+        return mbeanServer;
+    }
+
+    public void setMbeanServer(MBeanServer mbeanServer) {
+        this.mbeanServer = mbeanServer;
+    }
+
+    public void destroy() throws Exception {
+        // Using the array to hold the busMBeans to avoid the
+        // CurrentModificationException
+        Object[] mBeans = mbeans.toArray();
+        int caught = 0;
+        for (Object name : mBeans) {
+            mbeans.remove((ObjectName)name);
+            try {
+                unregister((ObjectName)name);
+            } catch (JMException jmex) {
+                LOG.info("Exception unregistering MBean", jmex);
+                caught++;
+            }
+        }
+        if (caught > 0) {
+            LOG.warn("A number of " + caught
+                     + " exceptions caught while unregistering MBeans during stop operation.  "
+                     + "See INFO log for details.");
+        }
+    }
+
+    public void register(Object obj, ObjectName name) throws JMException {
+        register(obj, name, false);
+    }
+
+    public void register(Object obj, ObjectName name, boolean forceRegistration) throws JMException {
+        try {
+            registerMBeanWithServer(obj, name, forceRegistration);
+        } catch (NotCompliantMBeanException e) {
+            // If this is not a "normal" MBean, then try to deploy it using JMX
+            // annotations
+            ModelMBeanInfo mbi = assembler.getMBeanInfo(obj, name.toString());
+            RequiredModelMBean mbean = (RequiredModelMBean) mbeanServer.instantiate(RequiredModelMBean.class.getName());
+            mbean.setModelMBeanInfo(mbi);
+            try {
+                mbean.setManagedResource(obj, "ObjectReference");
+            } catch (InvalidTargetObjectTypeException itotex) {
+                throw new JMException(itotex.getMessage());
+            }
+            registerMBeanWithServer(mbean, name, forceRegistration);
+        }
+    }
+
+    public synchronized void unregister(ObjectName name) throws JMException {
+        if (mbeans.contains(name)) {
+            //check if this bean already get removed in destory method
+            mbeanServer.unregisterMBean(name);
+        }
+    }
+
+    private void registerMBeanWithServer(Object obj, ObjectName name, boolean forceRegistration) throws JMException {
+        ObjectInstance instance = null;
+        try {
+            instance = mbeanServer.registerMBean(obj, name);
+        } catch (InstanceAlreadyExistsException e) {
+            if (forceRegistration) {
+                mbeanServer.unregisterMBean(name);
+                instance = mbeanServer.registerMBean(obj, name);
+            } 
+        } catch (NotCompliantMBeanException e) {
+            throw e;
+        }
+
+        if (instance != null) {
+            mbeans.add(name);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/NamingStrategy.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/NamingStrategy.java
new file mode 100644
index 0000000..7a03399
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/NamingStrategy.java
@@ -0,0 +1,35 @@
+/*
+ * 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.servicemix.kernel.gshell.features.management;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.apache.servicemix.kernel.gshell.features.FeaturesRegistry;
+
+public interface NamingStrategy {
+
+    ObjectName getObjectName(ManagedFeature feature) throws MalformedObjectNameException;
+
+    ObjectName getObjectName(ManagedFeature feature, boolean installed) throws MalformedObjectNameException;
+
+    ObjectName getObjectName(ManagedRepository component) throws MalformedObjectNameException;
+
+    String getJmxDomainName();
+
+    ObjectName getObjectName(FeaturesRegistry features) throws MalformedObjectNameException;
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/resources/META-INF/spring/gshell-features.xml b/karaf/gshell/gshell-features/src/main/resources/META-INF/spring/gshell-features.xml
new file mode 100644
index 0000000..d45b6be
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/resources/META-INF/spring/gshell-features.xml
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:ctx="http://www.springframework.org/schema/context"
+       xmlns:osgi="http://www.springframework.org/schema/osgi"
+       xmlns:osgix="http://www.springframework.org/schema/osgi-compendium"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+       xsi:schemaLocation="
+  http://www.springframework.org/schema/beans
+  http://www.springframework.org/schema/beans/spring-beans.xsd
+  http://www.springframework.org/schema/context
+  http://www.springframework.org/schema/context/spring-context.xsd
+  http://www.springframework.org/schema/osgi
+  http://www.springframework.org/schema/osgi/spring-osgi.xsd
+  http://www.springframework.org/schema/osgi-compendium
+  http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd
+  http://www.springframework.org/schema/util
+  http://www.springframework.org/schema/util/spring-util.xsd
+  http://servicemix.apache.org/schema/servicemix-gshell
+  http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+    <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+    
+    <bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+      <property name="location" value="file:${servicemix.home}/etc/org.apache.servicemix.features.cfg"/>
+    </bean>
+
+    <gshell:command-bundle>
+        <gshell:command name="features/addUrl">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.features.commands.AddUrlCommand" />
+        </gshell:command>
+        <gshell:command name="features/listUrl">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.features.commands.ListUrlCommand" />
+        </gshell:command>
+        <gshell:command name="features/removeUrl">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.features.commands.RemoveUrlCommand" />
+            <gshell:completers>
+                <ref bean="removeUrlCompleter" />
+            </gshell:completers>
+        </gshell:command>
+        <gshell:command name="features/refreshUrl">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.features.commands.RefreshUrlCommand" />
+        </gshell:command>
+        <gshell:command name="features/install">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.features.commands.InstallFeatureCommand" />
+            <gshell:completers>
+                <ref bean="installFeatureCompleter" /> 
+            </gshell:completers>
+        </gshell:command>
+        <gshell:command name="features/uninstall">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.features.commands.UninstallFeatureCommand" />
+            <gshell:completers>
+                <ref bean="uninstallFeatureCompleter" />
+            </gshell:completers>
+        </gshell:command>
+        <gshell:command name="features/list">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.features.commands.ListFeaturesCommand" />
+        </gshell:command>
+    </gshell:command-bundle>
+
+    <bean id="featuresService" class="org.apache.servicemix.kernel.gshell.features.internal.FeaturesServiceImpl" init-method="start" destroy-method="stop">
+        <property name="urls" value="${featuresRepositories}" />
+        <property name="boot" value="${featuresBoot}" />
+        <property name="configAdmin" ref="configAdmin" />
+        <property name="preferences" ref="preferences" />
+        <property name="featuresServiceRegistry" ref="featureServiceRegistry" />
+    </bean>
+
+    <bean id="featureDeploymentListener" class="org.apache.servicemix.kernel.gshell.features.internal.FeatureDeploymentListener">
+        <property name="featuresService" ref="featuresService" />
+    </bean>
+
+    <bean id="namingStrategy" class="org.apache.servicemix.kernel.gshell.features.management.DefaultNamingStrategy">
+        <property name="jmxDomainName" value="org.apache.servicemix" />
+    </bean>
+
+    <bean id="managementAgent" class="org.apache.servicemix.kernel.gshell.features.management.ManagementAgent">
+        <property name="mbeanServer" ref="mbeanServer" />
+    </bean>
+
+    <bean id="featureServiceRegistry" class="org.apache.servicemix.kernel.gshell.features.management.ManagedFeaturesRegistry">
+        <property name="managementAgent" ref="managementAgent" />
+        <property name="namingStrategy" ref="namingStrategy" />
+        <property name="featuresService" ref="featuresService" />
+    </bean>
+
+    <osgi:reference id="configAdmin" interface="org.osgi.service.cm.ConfigurationAdmin" />
+
+    <osgi:reference id="preferences" interface="org.osgi.service.prefs.PreferencesService" cardinality="0..1" />
+
+    <osgi:service ref="featuresService" interface="org.apache.servicemix.kernel.gshell.features.FeaturesService" />
+
+    <osgi:service ref="featureDeploymentListener" interface="org.apache.servicemix.kernel.filemonitor.DeploymentListener" />
+
+    <osgix:cm-properties id="cmProps" persistent-id="org.apache.servicemix.features">
+        <prop key="featuresRepositories"></prop>
+        <prop key="featuresBoot"></prop>
+    </osgix:cm-properties>
+
+    <!-- <ctx:property-placeholder properties-ref="cmProps" /> -->
+
+    <osgi:reference id="mbeanServer"
+                    interface="javax.management.MBeanServer"
+                    cardinality="0..1" >
+        <osgi:listener ref="featureServiceRegistry" bind-method="registerMBeanServer" />
+    </osgi:reference>
+
+    <bean id="installFeatureCompleter" class="org.apache.servicemix.kernel.gshell.features.completers.AvailableFeatureCompleter">
+        <property name="featuresRegistry" ref="featureServiceRegistry" />
+    </bean>
+
+    <bean id="uninstallFeatureCompleter" class="org.apache.servicemix.kernel.gshell.features.completers.InstalledFeatureCompleter">
+        <property name="featuresRegistry" ref="featureServiceRegistry" />
+    </bean>
+
+    <bean id="removeUrlCompleter" class="org.apache.servicemix.kernel.gshell.features.completers.FeatureRepositoryCompleter">
+        <property name="featuresRegistry" ref="featureServiceRegistry" />
+    </bean>
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/AddUrlCommand.properties b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/AddUrlCommand.properties
new file mode 100644
index 0000000..cf92ffe
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/AddUrlCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Add a list of repository URLs to the features service.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/InstallFeatureCommand.properties b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/InstallFeatureCommand.properties
new file mode 100644
index 0000000..97fcbe0
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/InstallFeatureCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Install a feature.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/ListFeaturesCommand.properties b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/ListFeaturesCommand.properties
new file mode 100644
index 0000000..4c807af
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/ListFeaturesCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=List existing features.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/ListUrlCommand.properties b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/ListUrlCommand.properties
new file mode 100644
index 0000000..a544da9
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/ListUrlCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Display the repository URLs currently associated with the features service.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/RefreshUrlCommand.properties b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/RefreshUrlCommand.properties
new file mode 100644
index 0000000..12687fd
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/RefreshUrlCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Reload the repositories to obtain a fresh list of features.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/RemoveUrlCommand.properties b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/RemoveUrlCommand.properties
new file mode 100644
index 0000000..34c2126
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/RemoveUrlCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Remove a list of repository URLs from the features service.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/UninstallFeatureCommand.properties b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/UninstallFeatureCommand.properties
new file mode 100644
index 0000000..ab3348e
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/UninstallFeatureCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Uninstall a feature.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/FeatureTest.java b/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/FeatureTest.java
new file mode 100644
index 0000000..97d6332
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/FeatureTest.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.features;
+
+import junit.framework.TestCase;
+import org.apache.servicemix.kernel.gshell.features.internal.FeatureImpl;
+
+public class FeatureTest extends TestCase {
+	
+	public void testValueOf() {
+		Feature feature = FeatureImpl.valueOf("name" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "version");
+		assertEquals(feature.getName(), "name");
+		assertEquals(feature.getVersion(), "version");
+		feature = FeatureImpl.valueOf("name");
+		assertEquals(feature.getName(), "name");
+		assertEquals(feature.getVersion(), FeatureImpl.DEFAULT_VERSION);
+	}
+	
+}
diff --git a/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/FeaturesServiceTest.java b/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/FeaturesServiceTest.java
new file mode 100644
index 0000000..5909722
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/FeaturesServiceTest.java
@@ -0,0 +1,700 @@
+/*
+ * 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.servicemix.kernel.gshell.features;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.net.URI;
+
+import junit.framework.TestCase;
+import org.apache.servicemix.kernel.gshell.features.internal.FeatureImpl;
+import org.apache.servicemix.kernel.gshell.features.internal.FeaturesServiceImpl;
+import org.apache.servicemix.kernel.gshell.features.management.ManagedFeaturesRegistry;
+import org.easymock.EasyMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.isA;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reset;
+import static org.easymock.EasyMock.verify;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.prefs.Preferences;
+import org.osgi.service.prefs.PreferencesService;
+import org.springframework.context.ApplicationContext;
+
+public class FeaturesServiceTest extends TestCase {
+
+    public void testInstallFeature() throws Exception {
+
+        String name = ApplicationContext.class.getName();
+        name = name.replace(".", "/")  + ".class";
+        name = getClass().getClassLoader().getResource(name).toString();
+        name = name.substring("jar:".length(), name.indexOf('!'));
+
+        File tmp = File.createTempFile("smx", ".feature");
+        PrintWriter pw = new PrintWriter(new FileWriter(tmp));
+        pw.println("<features>");
+        pw.println("  <feature name=\"f1\">");
+        pw.println("    <bundle>" + name + "</bundle>");
+        pw.println("  </feature>");
+        pw.println("</features>");
+        pw.close();
+
+        URI uri = tmp.toURI();
+
+        Preferences prefs = EasyMock.createMock(Preferences.class);
+        PreferencesService preferencesService = EasyMock.createMock(PreferencesService.class);
+        Preferences repositoriesNode = EasyMock.createMock(Preferences.class);
+        Preferences featuresNode = EasyMock.createMock(Preferences.class);
+        BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
+        Bundle installedBundle = EasyMock.createMock(Bundle.class);
+        FeaturesRegistry featuresRegistry = EasyMock.createNiceMock(FeaturesRegistry.class);
+
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 1);
+        repositoriesNode.put("item.0", uri.toString());
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();
+        featuresRegistry.register(isA(Repository.class));
+
+        replay(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+        FeaturesServiceImpl svc = new FeaturesServiceImpl();
+        svc.setPreferences(preferencesService);
+        svc.setBundleContext(bundleContext);
+        svc.setFeaturesServiceRegistry(featuresRegistry);
+        svc.addRepository(uri);
+        
+        Repository[] repositories = svc.listRepositories();
+        assertNotNull(repositories);
+        assertEquals(1, repositories.length);
+        assertNotNull(repositories[0]);
+        Feature[] features = repositories[0].getFeatures();
+        assertNotNull(features);
+        assertEquals(1, features.length);
+        assertNotNull(features[0]);
+        assertEquals("f1", features[0].getName());
+        assertNotNull(features[0].getDependencies());
+        assertEquals(0, features[0].getDependencies().size());
+        assertNotNull(features[0].getBundles());
+        assertEquals(1, features[0].getBundles().size());
+        assertEquals(name, features[0].getBundles().get(0));
+
+        verify(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+        reset(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+        expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+        expect(bundleContext.installBundle(isA(String.class),
+                                           isA(InputStream.class))).andReturn(installedBundle);
+        expect(installedBundle.getBundleId()).andReturn(12345L);
+        expect(bundleContext.getBundle(12345L)).andReturn(installedBundle);
+        installedBundle.start();
+
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 1);
+        repositoriesNode.put("item.0", uri.toString());
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();
+        featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + FeatureImpl.DEFAULT_VERSION, "12345");
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();
+
+        replay(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle);
+
+        svc.installFeature("f1");
+        
+        String[] installed = svc.listInstalledFeatures();
+        assertEquals(1, installed.length);
+        assertEquals("f1", installed[0]);
+    }
+
+    public void testUninstallFeature() throws Exception {
+    	
+        String name = ApplicationContext.class.getName();
+        name = name.replace(".", "/")  + ".class";
+        name = getClass().getClassLoader().getResource(name).toString();
+        name = name.substring("jar:".length(), name.indexOf('!'));
+
+        File tmp = File.createTempFile("smx", ".feature");
+        PrintWriter pw = new PrintWriter(new FileWriter(tmp));
+        pw.println("<features>");
+        pw.println("  <feature name=\"f1\" version=\"0.1\">");
+        pw.println("    <bundle>" + name + "</bundle>");
+        pw.println("  </feature>");
+        pw.println("  <feature name=\"f1\" version=\"0.2\">");
+        pw.println("    <bundle>" + name + "</bundle>");
+        pw.println("  </feature>");
+        pw.println("</features>");
+        pw.close();
+
+        URI uri = tmp.toURI();
+        
+        Preferences prefs = EasyMock.createMock(Preferences.class);
+        PreferencesService preferencesService = EasyMock.createMock(PreferencesService.class);
+        Preferences repositoriesNode = EasyMock.createMock(Preferences.class);
+        Preferences featuresNode = EasyMock.createMock(Preferences.class);
+        BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
+        Bundle installedBundle = EasyMock.createMock(Bundle.class);
+        FeaturesRegistry featuresRegistry = EasyMock.createNiceMock(FeaturesRegistry.class);
+
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 1);
+        repositoriesNode.put("item.0", uri.toString());
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();
+        featuresRegistry.register(isA(Repository.class));
+
+        replay(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+        FeaturesServiceImpl svc = new FeaturesServiceImpl();
+        svc.setPreferences(preferencesService);
+        svc.setBundleContext(bundleContext);
+        svc.setFeaturesServiceRegistry(featuresRegistry);
+        svc.addRepository(uri);
+        
+        verify(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+        reset(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+        // Installs f1 and 0.1
+        expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+        expect(bundleContext.installBundle(isA(String.class),
+                                           isA(InputStream.class))).andReturn(installedBundle);
+        expect(installedBundle.getBundleId()).andReturn(12345L);
+        expect(bundleContext.getBundle(12345L)).andReturn(installedBundle);
+        installedBundle.start();
+
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 1);
+        repositoriesNode.put("item.0", uri.toString());
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();
+        featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();
+        
+        // Installs f1 and 0.2
+        expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+        expect(bundleContext.installBundle(isA(String.class),
+                                           isA(InputStream.class))).andReturn(installedBundle);
+        expect(installedBundle.getBundleId()).andReturn(123456L);
+        expect(bundleContext.getBundle(123456L)).andReturn(installedBundle);
+        installedBundle.start();
+
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 1);
+        repositoriesNode.put("item.0", uri.toString());
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();
+        featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+        featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "123456");
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();
+
+        // UnInstalls f1 and 0.1
+        expect(bundleContext.getBundle(12345)).andReturn(installedBundle);
+        installedBundle.uninstall();
+
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 1);
+        repositoriesNode.put("item.0", uri.toString());
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();
+        featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "123456");
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();
+
+        // UnInstalls f1 and 0.2
+        expect(bundleContext.getBundle(123456)).andReturn(installedBundle);
+        installedBundle.uninstall();
+
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 1);
+        repositoriesNode.put("item.0", uri.toString());
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();
+
+        replay(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle);
+
+        try {
+            svc.uninstallFeature("f1");
+            fail("Uninstall should have failed as feature is not installed");
+        } catch (Exception e) {
+            // ok
+        }
+
+        svc.installFeature("f1", "0.1");
+        svc.installFeature("f1", "0.2");
+
+        try {
+            svc.uninstallFeature("f1");
+            fail("Uninstall should have failed as feature is installed in multiple versions");
+        } catch (Exception e) {
+            // ok
+        }
+
+        svc.uninstallFeature("f1", "0.1");
+        svc.uninstallFeature("f1");
+    }    
+    
+    // Tests Add and Remove Repository
+    public void testAddAndRemoveRepository() throws Exception {        
+
+    	String name = ApplicationContext.class.getName();
+        name = name.replace(".", "/")  + ".class";
+        name = getClass().getClassLoader().getResource(name).toString();
+        name = name.substring("jar:".length(), name.indexOf('!'));        
+
+        File tmp = File.createTempFile("smx", ".feature");
+        PrintWriter pw = new PrintWriter(new FileWriter(tmp));
+        pw.println("<features>");
+        pw.println("  <feature name=\"f1\" version=\"0.1\">");
+        pw.println("    <bundle>" + name + "</bundle>");
+        pw.println("  </feature>");
+        pw.println("  <feature name=\"f1\" version=\"0.2\">");
+        pw.println("    <bundle>" + name + "</bundle>");
+        pw.println("  </feature>");
+        pw.println("  <feature name=\"f2\" version=\"0.2\">");
+        pw.println("    <bundle>" + name + "</bundle>");
+        pw.println("  </feature>");
+        pw.println("</features>");
+        pw.close();
+
+        URI uri = tmp.toURI();
+
+        // loads the state
+        Preferences prefs = EasyMock.createMock(Preferences.class);
+        PreferencesService preferencesService = EasyMock.createMock(PreferencesService.class);
+        Preferences repositoriesNode = EasyMock.createMock(Preferences.class);
+        Preferences featuresNode = EasyMock.createMock(Preferences.class);
+        BundleContext bundleContext = EasyMock.createMock(BundleContext.class);        
+        Bundle installedBundle = EasyMock.createMock(Bundle.class);        
+        FeaturesRegistry featuresRegistry = EasyMock.createNiceMock(FeaturesRegistry.class);
+
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 1);
+        repositoriesNode.put("item.0", uri.toString());        
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();               
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();
+        
+        featuresRegistry.register(isA(Repository.class));
+        
+        // SaveState for addRepository
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 1);
+        repositoriesNode.put("item.0", uri.toString());        
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();
+        
+        // SaveState for removeRepository
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 0);              
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();        
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();
+        replay(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+        FeaturesServiceImpl svc = new FeaturesServiceImpl();
+        svc.setPreferences(preferencesService);
+        svc.setBundleContext(bundleContext);        
+        svc.setFeaturesServiceRegistry(featuresRegistry);
+        
+        // Adds Repository
+        svc.addRepository(uri);                                                     
+        
+        // Removes Repository
+        svc.removeRepository(uri);        
+    }
+
+    // Tests installing all features in a repo and uninstalling
+    // all features in a repo
+    public void testInstallUninstallAllFeatures() throws Exception {        
+
+    	String name = ApplicationContext.class.getName();
+        name = name.replace(".", "/")  + ".class";
+        name = getClass().getClassLoader().getResource(name).toString();
+        name = name.substring("jar:".length(), name.indexOf('!'));        
+
+        File tmp = File.createTempFile("smx", ".feature");
+        PrintWriter pw = new PrintWriter(new FileWriter(tmp));
+        pw.println("<features>");
+        pw.println("  <feature name=\"f1\" version=\"0.1\">");
+        pw.println("    <bundle>" + name + "</bundle>");
+        pw.println("  </feature>");
+        pw.println("  <feature name=\"f1\" version=\"0.2\">");
+        pw.println("    <bundle>" + name + "</bundle>");
+        pw.println("  </feature>");
+        pw.println("  <feature name=\"f2\" version=\"0.2\">");
+        pw.println("    <bundle>" + name + "</bundle>");
+        pw.println("  </feature>");
+        pw.println("</features>");
+        pw.close();
+
+        URI uri = tmp.toURI();
+
+        // loads the state
+        Preferences prefs = EasyMock.createMock(Preferences.class);
+        PreferencesService preferencesService = EasyMock.createMock(PreferencesService.class);
+        Preferences repositoriesNode = EasyMock.createMock(Preferences.class);
+        Preferences featuresNode = EasyMock.createMock(Preferences.class);
+        BundleContext bundleContext = EasyMock.createMock(BundleContext.class);        
+        Bundle installedBundle = EasyMock.createMock(Bundle.class);        
+        FeaturesRegistry featuresRegistry = EasyMock.createNiceMock(FeaturesRegistry.class);
+
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 1);
+        repositoriesNode.put("item.0", uri.toString());        
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();
+        
+        // Installs first feature name = f1, version = 0.1 
+        expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+        expect(bundleContext.installBundle(isA(String.class),
+                                           isA(InputStream.class))).andReturn(installedBundle);
+        expect(installedBundle.getBundleId()).andReturn(12345L);
+        expect(bundleContext.getBundle(12345L)).andReturn(installedBundle);
+        installedBundle.start();
+        
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 1);
+        repositoriesNode.put("item.0", uri.toString());        
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();
+        featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();
+        
+        // Installs second feature name = f1, version = 0.2
+        expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+        expect(bundleContext.installBundle(isA(String.class),
+                                           isA(InputStream.class))).andReturn(installedBundle);
+        expect(installedBundle.getBundleId()).andReturn(123456L);
+        expect(bundleContext.getBundle(123456L)).andReturn(installedBundle);
+        installedBundle.start();
+        
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 1);
+        repositoriesNode.put("item.0", uri.toString());        
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();
+        featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+        featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "123456");
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();
+        
+        // Installs third feature name = f2, version = 0.2
+        expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+        expect(bundleContext.installBundle(isA(String.class),
+                                           isA(InputStream.class))).andReturn(installedBundle);
+        expect(installedBundle.getBundleId()).andReturn(1234567L);
+        expect(bundleContext.getBundle(1234567L)).andReturn(installedBundle);
+        installedBundle.start();
+        
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 1);
+        repositoriesNode.put("item.0", uri.toString());        
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();
+        featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+        featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "123456");
+        featuresNode.put("f2" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "1234567");
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();
+        
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 0);
+        repositoriesNode.put("item.0", uri.toString());        
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();
+        featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+        featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "123456");
+        featuresNode.put("f2" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "1234567");
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();
+
+        // uninstallAllFeatures 
+        
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 1);
+        repositoriesNode.put("item.0", uri.toString());        
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();
+        featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+        featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "123456");
+        featuresNode.put("f2" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "1234567");
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();
+        
+        // uninstalls first feature name = f1, version = 0.1
+        expect(bundleContext.getBundle(12345)).andReturn(installedBundle);
+        installedBundle.uninstall();
+
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 1);
+        repositoriesNode.put("item.0", uri.toString());        
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();
+        featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "123456");
+        featuresNode.put("f2" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "1234567");
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();
+        
+        // uninstalls third feature name = f2, version = 0.2
+        expect(bundleContext.getBundle(1234567)).andReturn(installedBundle);
+        installedBundle.uninstall();
+
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 1);
+        repositoriesNode.put("item.0", uri.toString());        
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();        
+        featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "123456");
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();
+
+        // uninstalls second feature name = f1, version = 0.2
+        expect(bundleContext.getBundle(123456)).andReturn(installedBundle);
+        installedBundle.uninstall();
+
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 1);
+        repositoriesNode.put("item.0", uri.toString());        
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();        
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();
+
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 0);
+        repositoriesNode.put("item.0", uri.toString());        
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();
+
+        replay(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+        FeaturesServiceImpl svc = new FeaturesServiceImpl();
+        svc.setPreferences(preferencesService);
+        svc.setBundleContext(bundleContext);        
+        svc.setFeaturesServiceRegistry(featuresRegistry);                
+        svc.installAllFeatures(uri);                            
+        
+        // Uninstalls features with versions.
+        svc.uninstallAllFeatures(uri);    
+    }    
+
+
+    // Tests install of a Repository that includes a feature 
+    // with a feature dependency  
+    // The dependant feature is in the same repository 
+    // Tests uninstall of features
+    public void testInstallFeatureWithDependantFeatures() throws Exception {          
+
+    	String name = ApplicationContext.class.getName();
+        name = name.replace(".", "/")  + ".class";
+        name = getClass().getClassLoader().getResource(name).toString();
+        name = name.substring("jar:".length(), name.indexOf('!'));        
+
+        File tmp = File.createTempFile("smx", ".feature");
+        PrintWriter pw = new PrintWriter(new FileWriter(tmp));
+        pw.println("<features>");
+        pw.println("  <feature name=\"f1\" version=\"0.1\">");
+        pw.println("  <feature version=\"0.1\">f2</feature>");
+        pw.println("    <bundle>" + name + "</bundle>");
+        pw.println("  </feature>");
+        pw.println("  <feature name=\"f2\" version=\"0.1\">");
+        pw.println("    <bundle>" + name + "</bundle>");
+        pw.println("  </feature>");
+        pw.println("</features>");
+        pw.close();
+
+        URI uri = tmp.toURI();
+
+        // loads the state
+        Preferences prefs = EasyMock.createMock(Preferences.class);
+        PreferencesService preferencesService = EasyMock.createMock(PreferencesService.class);
+        Preferences repositoriesNode = EasyMock.createMock(Preferences.class);
+        Preferences repositoriesAvailableNode = EasyMock.createMock(Preferences.class);
+        Preferences featuresNode = EasyMock.createMock(Preferences.class);
+        BundleContext bundleContext = EasyMock.createMock(BundleContext.class);        
+        Bundle installedBundle = EasyMock.createMock(Bundle.class);        
+        FeaturesRegistry featuresRegistry = EasyMock.createNiceMock(FeaturesRegistry.class);        
+
+        // savestate from addRepository
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 1);
+        repositoriesNode.put("item.0", uri.toString());        
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();
+
+        // Installs feature f1 with dependency on f2
+        // so will install f2 first
+        expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+        expect(bundleContext.installBundle(isA(String.class),
+                                           isA(InputStream.class))).andReturn(installedBundle);
+        expect(installedBundle.getBundleId()).andReturn(12345L);
+        expect(bundleContext.getBundle(12345L)).andReturn(installedBundle);
+        installedBundle.start();
+        
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 1);
+        repositoriesNode.put("item.0", uri.toString());        
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();
+        featuresNode.put("f2" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();
+        
+        // Then installs f1
+        expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+        expect(bundleContext.installBundle(isA(String.class),
+                                           isA(InputStream.class))).andReturn(installedBundle);
+        expect(installedBundle.getBundleId()).andReturn(1234L);
+        expect(bundleContext.getBundle(1234L)).andReturn(installedBundle);
+        installedBundle.start();
+        
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 1);
+        repositoriesNode.put("item.0", uri.toString());        
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();
+        featuresNode.put("f2" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+        featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "1234");
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();                      
+
+        // uninstalls first feature name = f1, version = 0.1
+        expect(bundleContext.getBundle(1234)).andReturn(installedBundle);
+        installedBundle.uninstall();
+
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 1);
+        repositoriesNode.put("item.0", uri.toString());        
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();
+        featuresNode.put("f2" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");        
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();
+        
+       // uninstalls first feature name = f2, version = 0.1
+        expect(bundleContext.getBundle(12345)).andReturn(installedBundle);
+        installedBundle.uninstall();
+
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 1);
+        repositoriesNode.put("item.0", uri.toString());        
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();        
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();                        
+        
+        expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+        expect(prefs.node("repositories")).andReturn(repositoriesNode);
+        repositoriesNode.clear();
+        repositoriesNode.putInt("count", 0);
+        repositoriesNode.put("item.0", uri.toString());        
+        expect(prefs.node("features")).andReturn(featuresNode);
+        featuresNode.clear();        
+        prefs.putBoolean("bootFeaturesInstalled", false);
+        prefs.flush();                        
+        
+        replay(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+        FeaturesServiceImpl svc = new FeaturesServiceImpl();
+        svc.setPreferences(preferencesService);
+        svc.setBundleContext(bundleContext);        
+        svc.setFeaturesServiceRegistry(featuresRegistry);                
+        svc.addRepository(uri);    
+
+        svc.installFeature("f1", "0.1");
+                
+        // Uninstall repository
+        svc.uninstallFeature("f1", "0.1");
+        svc.uninstallFeature("f2", "0.1");
+        
+    }
+
+}
diff --git a/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/RepositoryTest.java b/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/RepositoryTest.java
new file mode 100644
index 0000000..e8cfffd
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/RepositoryTest.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.features;
+
+import java.net.URI;
+
+import junit.framework.TestCase;
+import org.apache.servicemix.kernel.gshell.features.internal.FeatureImpl;
+import org.apache.servicemix.kernel.gshell.features.internal.RepositoryImpl;
+
+
+public class RepositoryTest extends TestCase {
+
+    public void testLoad() throws Exception {
+        RepositoryImpl r = new RepositoryImpl(getClass().getResource("repo1.xml").toURI());
+        // Check repo
+        URI[] repos = r.getRepositories();
+        assertNotNull(repos);
+        assertEquals(1, repos.length);
+        assertEquals(URI.create("urn:r1"), repos[0]);
+        // Check features
+        Feature[] features = r.getFeatures();
+        assertNotNull(features);
+        assertEquals(2, features.length);
+        assertNotNull(features[0]);
+        assertEquals("f1", features[0].getName());
+        assertNotNull(features[0].getConfigurations());
+        assertEquals(1, features[0].getConfigurations().size());
+        assertNotNull(features[0].getConfigurations().get("c1"));
+        assertEquals(1, features[0].getConfigurations().get("c1").size());
+        assertEquals("v", features[0].getConfigurations().get("c1").get("k"));
+        assertNotNull(features[0].getDependencies());
+        assertEquals(0, features[0].getDependencies().size());
+        assertNotNull(features[0].getBundles());
+        assertEquals(2, features[0].getBundles().size());
+        assertEquals("b1", features[0].getBundles().get(0));
+        assertEquals("b2", features[0].getBundles().get(1));
+        assertNotNull(features[1]);
+        assertEquals("f2", features[1].getName());
+        assertNotNull(features[1].getConfigurations());
+        assertEquals(0, features[1].getConfigurations().size());
+        assertNotNull(features[1].getDependencies());
+        assertEquals(1, features[1].getDependencies().size());
+        assertEquals("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + FeatureImpl.DEFAULT_VERSION, features[1].getDependencies().get(0).toString());
+        assertNotNull(features[1].getBundles());
+        assertEquals(1, features[1].getBundles().size());
+        assertEquals("b3", features[1].getBundles().get(0));
+    }
+    
+    public void testShowWrongUriInException() throws Exception {
+        String uri = "src/test/resources/org/apache/servicemix/kernel/gshell/features/repo1.xml";
+        RepositoryImpl r = new RepositoryImpl(new URI(uri));
+        try {
+            r.load();
+        } catch (Exception e) {
+            assertTrue(e.getMessage().contains(uri));
+        }
+    }
+}
diff --git a/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/internal/FeaturesServiceImplTest.java b/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/internal/FeaturesServiceImplTest.java
new file mode 100644
index 0000000..8ee87de
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/internal/FeaturesServiceImplTest.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.features.internal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+import org.apache.servicemix.kernel.gshell.features.Feature;
+
+/**
+ * Test cases for {@link FeaturesServiceImpl}
+ */
+public class FeaturesServiceImplTest extends TestCase {
+    
+    public void testGetFeature() throws Exception {
+        final Map<String, Map<String, Feature>> features = new HashMap<String, Map<String,Feature>>();
+        Map<String, Feature> versions = new HashMap<String, Feature>();
+        FeatureImpl feature = new FeatureImpl("transaction");
+        versions.put("1.0.0", feature);
+        features.put("transaction", versions);
+        final FeaturesServiceImpl impl = new FeaturesServiceImpl() {
+            protected Map<String,Map<String,Feature>> getFeatures() throws Exception {
+                return features;
+            };
+        };
+        assertNotNull(impl.getFeature("transaction", FeatureImpl.DEFAULT_VERSION));
+        assertSame(feature, impl.getFeature("transaction", FeatureImpl.DEFAULT_VERSION));
+    }
+    
+    public void testGetFeatureNotAvailable() throws Exception {
+        final Map<String, Map<String, Feature>> features = new HashMap<String, Map<String,Feature>>();
+        Map<String, Feature> versions = new HashMap<String, Feature>();
+        versions.put("1.0.0", new FeatureImpl("transaction"));
+        features.put("transaction", versions);
+        final FeaturesServiceImpl impl = new FeaturesServiceImpl() {
+            protected Map<String,Map<String,Feature>> getFeatures() throws Exception {
+                return features;
+            };
+        };
+        assertNull(impl.getFeature("activemq", FeatureImpl.DEFAULT_VERSION));
+    }
+    
+    public void testGetFeatureHighestAvailable() throws Exception {
+        final Map<String, Map<String, Feature>> features = new HashMap<String, Map<String,Feature>>();
+        Map<String, Feature> versions = new HashMap<String, Feature>();
+        versions.put("1.0.0", new FeatureImpl("transaction", "1.0.0"));
+        versions.put("2.0.0", new FeatureImpl("transaction", "2.0.0"));
+        features.put("transaction", versions);
+        final FeaturesServiceImpl impl = new FeaturesServiceImpl() {
+            protected Map<String,Map<String,Feature>> getFeatures() throws Exception {
+                return features;
+            };
+        };
+        assertNotNull(impl.getFeature("transaction", FeatureImpl.DEFAULT_VERSION));
+        assertSame("2.0.0", impl.getFeature("transaction", FeatureImpl.DEFAULT_VERSION).getVersion());
+    }
+
+}
diff --git a/karaf/gshell/gshell-features/src/test/resources/org/apache/servicemix/kernel/gshell/features/repo1.xml b/karaf/gshell/gshell-features/src/test/resources/org/apache/servicemix/kernel/gshell/features/repo1.xml
new file mode 100644
index 0000000..ea21df9
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/test/resources/org/apache/servicemix/kernel/gshell/features/repo1.xml
@@ -0,0 +1,31 @@
+<!--
+
+    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.
+-->
+<features>
+    <repository>urn:r1</repository>
+    <feature name="f1">
+        <config name="c1">
+            k=v
+        </config>
+        <bundle>b1</bundle>
+        <bundle>b2</bundle>
+    </feature>
+    <feature name="f2">
+        <feature>f1</feature>
+        <bundle>b3</bundle>
+    </feature>
+</features>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-log/pom.xml b/karaf/gshell/gshell-log/pom.xml
new file mode 100644
index 0000000..04ca7bd
--- /dev/null
+++ b/karaf/gshell/gshell-log/pom.xml
@@ -0,0 +1,95 @@
+<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">
+
+    <!--
+
+        Licensed to the Apache Software Foundation (ASF) under one or more
+        contributor license agreements.  See the NOTICE file distributed with
+        this work for additional information regarding copyright ownership.
+        The ASF licenses this file to You under the Apache License, Version 2.0
+        (the "License"); you may not use this file except in compliance with
+        the License.  You may obtain a copy of the License at
+
+           http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.servicemix.kernel.gshell</groupId>
+        <artifactId>gshell</artifactId>
+        <version>1.2.0-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.apache.servicemix.kernel.gshell</groupId>
+    <artifactId>org.apache.servicemix.kernel.gshell.log</artifactId>
+    <packaging>bundle</packaging>
+    <version>1.2.0-SNAPSHOT</version>
+    <name>Apache ServiceMix Kernel :: GShell Log Commands</name>
+
+    <description>
+        Provides the OSGi Log commands
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.servicemix.kernel.gshell</groupId>
+            <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.osgi</groupId>
+            <artifactId>spring-osgi-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.ops4j.pax.logging</groupId>
+            <artifactId>pax-logging-service</artifactId>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+                        <Export-Package>
+                            org.apache.servicemix.kernel.gshell.log*;version=${project.version};-split-package:=merge-first
+                        </Export-Package>
+                        <Import-Package>
+                            org.apache.geronimo.gshell.wisdom.command,
+                            org.apache.geronimo.gshell.wisdom.registry,
+                            org.apache.servicemix.kernel.gshell.core,
+                            org.ops4j.pax.logging,
+                            *
+                        </Import-Package>
+                        <Private-Package>!*</Private-Package>
+                        <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/DisplayException.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/DisplayException.java
new file mode 100644
index 0000000..917e01c
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/DisplayException.java
@@ -0,0 +1,52 @@
+/*
+ * 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.servicemix.kernel.gshell.log;
+
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.ops4j.pax.logging.spi.PaxLoggingEvent;
+
+public class DisplayException extends OsgiCommandSupport {
+
+    protected LruList<PaxLoggingEvent> events;
+
+    public LruList<PaxLoggingEvent> getEvents() {
+        return events;
+    }
+
+    public void setEvents(LruList<PaxLoggingEvent> events) {
+        this.events = events;
+    }
+
+    protected Object doExecute() throws Exception {
+        PaxLoggingEvent throwableEvent = null;
+        Iterable<PaxLoggingEvent> le = events.getElements(Integer.MAX_VALUE);
+        for (PaxLoggingEvent event : le) {
+            if (event.getThrowableStrRep() != null) {
+                throwableEvent = event;
+                // Do not break, as we iterate from the oldest to the newest event
+            }
+        }
+        if (throwableEvent != null) {
+            for (String r : throwableEvent.getThrowableStrRep()) {
+                io.out.println(r);
+            }
+            io.out.println();
+        }
+        return Result.SUCCESS;
+    }
+
+}
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/DisplayLog.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/DisplayLog.java
new file mode 100644
index 0000000..3beff07
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/DisplayLog.java
@@ -0,0 +1,78 @@
+/*
+ * 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.servicemix.kernel.gshell.log;
+
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.apache.servicemix.kernel.gshell.log.layout.PatternConverter;
+import org.apache.servicemix.kernel.gshell.log.layout.PatternParser;
+import org.ops4j.pax.logging.spi.PaxLoggingEvent;
+
+/**
+ * Displays the last log entries
+ */
+public class DisplayLog extends OsgiCommandSupport {
+
+    @Option(name = "-n", description="Number of entries to display")
+    protected int entries;
+
+    @Option(name = "-p", description="Output formatting pattern")
+    protected String overridenPattern;
+
+    protected String pattern;
+
+    protected LruList<PaxLoggingEvent> events;
+
+    public LruList<PaxLoggingEvent> getEvents() {
+        return events;
+    }
+
+    public void setEvents(LruList<PaxLoggingEvent> events) {
+        this.events = events;
+    }
+
+    public String getPattern() {
+        return pattern;
+    }
+
+    public void setPattern(String pattern) {
+        this.pattern = pattern;
+    }
+
+    protected Object doExecute() throws Exception {
+        PatternConverter cnv = new PatternParser(overridenPattern != null ? overridenPattern : pattern).parse();
+
+        Iterable<PaxLoggingEvent> le = events.getElements(entries == 0 ? Integer.MAX_VALUE : entries);
+        StringBuffer sb = new StringBuffer();
+        for (PaxLoggingEvent event : le) {
+            sb.setLength(0);
+            for (PatternConverter pc = cnv; pc != null; pc = pc.next) {
+                pc.format(sb, event);
+            }
+            io.out.print(sb.toString());
+            if (event.getThrowableStrRep() != null) {
+                for (String r : event.getThrowableStrRep()) {
+                    io.out.println(r);
+                }
+            }
+        }
+        io.out.println();
+        
+        return Result.SUCCESS;
+    }
+
+}
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/GetLogLevel.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/GetLogLevel.java
new file mode 100644
index 0000000..cf93797
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/GetLogLevel.java
@@ -0,0 +1,117 @@
+/*
+ * 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.servicemix.kernel.gshell.log;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+/**
+ * Get the log level for a given logger
+ */
+public class GetLogLevel extends OsgiCommandSupport {
+
+    @Argument(required = false, description = "Logger name, ALL or ROOT (default)")
+    String logger;
+
+    static final String CONFIGURATION_PID  = "org.ops4j.pax.logging";
+    static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger";
+    static final String LOGGER_PREFIX      = "log4j.logger.";
+    static final String ALL_LOGGER         = "ALL";
+    static final String ROOT_LOGGER        = "ROOT";
+
+    protected Object doExecute() throws Exception {
+        ConfigurationAdmin cfgAdmin = getConfigAdmin();
+        Configuration cfg = cfgAdmin.getConfiguration(CONFIGURATION_PID, null);
+        Dictionary props = cfg.getProperties();
+
+        if (ROOT_LOGGER.equalsIgnoreCase(this.logger)) {
+            this.logger = null;
+        }
+        if (ALL_LOGGER.equalsIgnoreCase(logger)) {
+            String root = getLevel((String) props.get(ROOT_LOGGER_PREFIX));
+            Map<String, String> loggers = new TreeMap<String, String>();
+            for (Enumeration e = props.keys(); e.hasMoreElements();) {
+                String prop = (String) e.nextElement();
+                if (prop.startsWith(LOGGER_PREFIX)) {
+                    String val = getLevel((String) props.get(prop));
+                    loggers.put(prop.substring(LOGGER_PREFIX.length()), val);
+                }
+            }
+            io.out.println("ROOT: " + root);
+            for (String logger : loggers.keySet()) {
+                io.out.println(logger + ": " + loggers.get(logger));
+            }
+        } else {
+            String logger = this.logger;
+            String val;
+            for (;;) {
+                String prop;
+                if (logger == null) {
+                    prop = ROOT_LOGGER_PREFIX;
+                } else {
+                    prop = LOGGER_PREFIX + logger;
+                }
+                val = (String) props.get(prop);
+                val = getLevel(val);
+                if (val != null || logger == null) {
+                    break;
+                }
+                int idx = logger.lastIndexOf('.');
+                if (idx < 0) {
+                    logger = null;
+                } else {
+                    logger = logger.substring(0, idx);
+                }
+            }
+            String st = "Level: " + val;
+            if (logger != this.logger) {
+                st += " (inherited from " + (logger != null ? logger : "ROOT") + ")";
+            }
+            io.out.println(st);
+        }
+        return Result.SUCCESS;
+    }
+
+    protected String getLevel(String prop) {
+        if (prop == null) {
+            return null;
+        } else {
+            String val = prop.trim();
+            int idx = val.indexOf(",");
+            if (idx == 0) {
+                val = null;
+            } else if (idx > 0) {
+                val = val.substring(0, idx);
+            }
+            return val;
+        }
+    }
+
+    protected ConfigurationAdmin getConfigAdmin() {
+        ServiceReference ref = getBundleContext().getServiceReference(ConfigurationAdmin.class.getName());
+        return getService(ConfigurationAdmin.class, ref);
+    }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/LruList.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/LruList.java
new file mode 100644
index 0000000..a214a17
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/LruList.java
@@ -0,0 +1,99 @@
+/*
+ * 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.servicemix.kernel.gshell.log;
+
+import java.util.Arrays;
+
+/**
+ * A list that only keep the last N elements added
+ */
+public class LruList<E> {
+
+    private E[] elements;
+    private transient int start = 0;
+    private transient int end = 0;
+    private transient boolean full = false;
+    private final int maxElements;
+
+    public LruList(int size) {
+        if (size <= 0) {
+            throw new IllegalArgumentException("The size must be greater than 0");
+        }
+        elements = (E[]) new Object[size];
+        maxElements = elements.length;
+    }
+
+    public int size() {
+        synchronized (elements) {
+            int size = 0;
+            if (end < start) {
+                size = maxElements - start + end;
+            } else if (end == start) {
+                size = (full ? maxElements : 0);
+            } else {
+                size = end - start;
+            }
+            return size;
+        }
+    }
+
+    public void add(E element) {
+        synchronized (elements) {
+            if (null == element) {
+                 throw new NullPointerException("Attempted to add null object to buffer");
+            }
+            if (size() == maxElements) {
+                Object e = elements[start];
+                if (null != e) {
+                    elements[start++] = null;
+                    if (start >= maxElements) {
+                        start = 0;
+                    }
+                    full = false;
+                }
+            }
+            elements[end++] = element;
+            if (end >= maxElements) {
+                end = 0;
+            }
+            if (end == start) {
+                full = true;
+            }
+        }
+    }
+
+    public Iterable<E> getElements() {
+        synchronized (elements) {
+            return getElements(size());
+        }
+    }
+
+    public Iterable<E> getElements(int nb) {
+        synchronized (elements) {
+            int s = size();
+            nb = Math.min(Math.max(0, nb), s);
+            E[] e = (E[]) new Object[nb];
+            for (int i = 0; i < nb; i++) {
+                e[i] = elements[(i + s - nb + start) % maxElements];
+            }
+            return Arrays.asList(e);
+        }
+    }
+
+}
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/SetLogLevel.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/SetLogLevel.java
new file mode 100644
index 0000000..7da0f7f
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/SetLogLevel.java
@@ -0,0 +1,119 @@
+/*
+ * 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.servicemix.kernel.gshell.log;
+
+import java.util.Dictionary;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+/**
+ * Set the log level for a given logger
+ */
+public class SetLogLevel extends OsgiCommandSupport {
+
+    @Argument(index = 0, required = true, description = "Level (TRACE, DEBUG, INFO, WARN, ERROR or - to unset")
+    String level;
+
+    @Argument(index = 1, required = false, description = "Logger name or ROOT (default)")
+    String logger;
+
+    static final String CONFIGURATION_PID  = "org.ops4j.pax.logging";
+    static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger";
+    static final String LOGGER_PREFIX      = "log4j.logger.";
+    static final String ROOT_LOGGER        = "ROOT";
+
+    static final String TRACE = "TRACE";
+    static final String DEBUG = "DEBUG";
+    static final String INFO = "INFO";
+    static final String WARN = "WARN";
+    static final String ERROR = "ERROR";
+    static final String INHERITED = "-";
+
+    protected Object doExecute() throws Exception {
+        if (ROOT_LOGGER.equalsIgnoreCase(this.logger)) {
+            this.logger = null;
+        }
+        if (!TRACE.equals(level) &&
+                !DEBUG.equals(level) &&
+                !INFO.equals(level) &&
+                !WARN.equals(level) &&
+                !ERROR.equals(level) &&
+                !INHERITED.equals(level)) {
+            io.err.println("level must be set to TRACE, DEBUG, INFO, WARN or ERROR (or - to unset it)");
+            return Result.FAILURE;
+        }
+        if (INHERITED.equals(level) && logger == null) {
+            io.err.println("Can not unset the ROOT logger");
+            return Result.FAILURE;
+        }
+
+        ConfigurationAdmin cfgAdmin = getConfigAdmin();
+        Configuration cfg = cfgAdmin.getConfiguration(CONFIGURATION_PID, null);
+        Dictionary props = cfg.getProperties();
+
+        String logger = this.logger;
+        String val;
+        String prop;
+        if (logger == null) {
+            prop = ROOT_LOGGER_PREFIX;
+        } else {
+            prop = LOGGER_PREFIX + logger;
+        }
+        val = (String) props.get(prop);
+        if (INHERITED.equals(level)) {
+            if (val != null) {
+                val = val.trim();
+                int idx = val.indexOf(",");
+                if (idx > 0) {
+                    val = val.substring(idx);
+                } else {
+                    val = null;
+                }
+            }
+        } else {
+            if (val == null) {
+                val = level;
+            } else {
+                val = val.trim();
+                int idx = val.indexOf(",");
+                if (idx == 0) {
+                    val = level + val;
+                } else if (idx > 0) {
+                    val = level + val.substring(idx);
+                }
+            }
+        }
+        if (val == null) {
+            props.remove(prop);
+        } else {
+            props.put(prop, val);
+        }
+        cfg.update(props);
+
+        return Result.SUCCESS;
+    }
+
+    protected ConfigurationAdmin getConfigAdmin() {
+        ServiceReference ref = getBundleContext().getServiceReference(ConfigurationAdmin.class.getName());
+        return getService(ConfigurationAdmin.class, ref);
+    }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/VmLogAppender.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/VmLogAppender.java
new file mode 100644
index 0000000..a1607b4
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/VmLogAppender.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.servicemix.kernel.gshell.log;
+
+import org.ops4j.pax.logging.spi.PaxAppender;
+import org.ops4j.pax.logging.spi.PaxLoggingEvent;
+
+/**
+ * A Pax Logging appender that keep a list of last events
+ */
+public class VmLogAppender implements PaxAppender {
+
+    protected LruList<PaxLoggingEvent> events;
+
+    public LruList<PaxLoggingEvent> getEvents() {
+        return events;
+    }
+
+    public void setEvents(LruList<PaxLoggingEvent> events) {
+        this.events = events;
+    }
+
+    public void doAppend(PaxLoggingEvent event) {
+        if (events != null) {
+            events.add(event);
+        }
+    }
+
+}
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/AbsoluteTimeDateFormat.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/AbsoluteTimeDateFormat.java
new file mode 100644
index 0000000..03b81ff
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/AbsoluteTimeDateFormat.java
@@ -0,0 +1,145 @@
+/*
+ * 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.servicemix.kernel.gshell.log.layout;
+
+import java.text.DateFormat;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * Copied from log4j
+ */
+/**
+   Formats a {@link Date} in the format "HH:mm:ss,SSS" for example,
+   "15:49:37,459".
+
+   @author Ceki G&uuml;lc&uuml;
+   @author Andrew Vajoczki
+
+   @since 0.7.5
+*/
+public class AbsoluteTimeDateFormat extends DateFormat {
+
+  /**
+     String constant used to specify {@link
+     org.apache.log4j.helpers.AbsoluteTimeDateFormat} in layouts. Current
+     value is <b>ABSOLUTE</b>.  */
+  public final static String ABS_TIME_DATE_FORMAT = "ABSOLUTE";
+
+  /**
+     String constant used to specify {@link
+     org.apache.log4j.helpers.DateTimeDateFormat} in layouts.  Current
+     value is <b>DATE</b>.
+  */
+  public final static String DATE_AND_TIME_DATE_FORMAT = "DATE";
+
+  /**
+     String constant used to specify {@link
+     org.apache.log4j.helpers.ISO8601DateFormat} in layouts. Current
+     value is <b>ISO8601</b>.
+  */
+  public final static String ISO8601_DATE_FORMAT = "ISO8601";
+
+  public
+  AbsoluteTimeDateFormat() {
+    setCalendar(Calendar.getInstance());
+  }
+
+  public
+  AbsoluteTimeDateFormat(TimeZone timeZone) {
+    setCalendar(Calendar.getInstance(timeZone));
+  }
+
+  private static long   previousTime;
+  private static char[] previousTimeWithoutMillis = new char[9]; // "HH:mm:ss."
+
+  /**
+     Appends to <code>sbuf</code> the time in the format
+     "HH:mm:ss,SSS" for example, "15:49:37,459"
+
+     @param date the date to format
+     @param sbuf the string buffer to write to
+     @param fieldPosition remains untouched
+    */
+  public
+  StringBuffer format(Date date, StringBuffer sbuf,
+		      FieldPosition fieldPosition) {
+
+    long now = date.getTime();
+    int millis = (int)(now % 1000);
+
+    if ((now - millis) != previousTime) {
+      // We reach this point at most once per second
+      // across all threads instead of each time format()
+      // is called. This saves considerable CPU time.
+
+      calendar.setTime(date);
+
+      int start = sbuf.length();
+
+      int hour = calendar.get(Calendar.HOUR_OF_DAY);
+      if(hour < 10) {
+	sbuf.append('0');
+      }
+      sbuf.append(hour);
+      sbuf.append(':');
+
+      int mins = calendar.get(Calendar.MINUTE);
+      if(mins < 10) {
+	sbuf.append('0');
+      }
+      sbuf.append(mins);
+      sbuf.append(':');
+
+      int secs = calendar.get(Calendar.SECOND);
+      if(secs < 10) {
+	sbuf.append('0');
+      }
+      sbuf.append(secs);
+      sbuf.append(',');
+
+      // store the time string for next time to avoid recomputation
+      sbuf.getChars(start, sbuf.length(), previousTimeWithoutMillis, 0);
+
+      previousTime = now - millis;
+    }
+    else {
+      sbuf.append(previousTimeWithoutMillis);
+    }
+
+
+
+    if(millis < 100)
+      sbuf.append('0');
+    if(millis < 10)
+      sbuf.append('0');
+
+    sbuf.append(millis);
+    return sbuf;
+  }
+
+  /**
+     This method does not do anything but return <code>null</code>.
+   */
+  public
+  Date parse(String s, ParsePosition pos) {
+    return null;
+  }
+}
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/DateTimeDateFormat.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/DateTimeDateFormat.java
new file mode 100644
index 0000000..0412ac8
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/DateTimeDateFormat.java
@@ -0,0 +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.
+ */
+package org.apache.servicemix.kernel.gshell.log.layout;
+
+import java.text.DateFormatSymbols;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * Copied from log4j
+ */
+/**
+   Formats a {@link Date} in the format "dd MMM yyyy HH:mm:ss,SSS" for example,
+   "06 Nov 1994 15:49:37,459".
+
+   @author Ceki G&uuml;lc&uuml;
+   @since 0.7.5
+*/
+public class DateTimeDateFormat extends AbsoluteTimeDateFormat {
+
+  String[] shortMonths;
+
+  public
+  DateTimeDateFormat() {
+    super();
+    shortMonths = new DateFormatSymbols().getShortMonths();
+  }
+
+  public
+  DateTimeDateFormat(TimeZone timeZone) {
+    this();
+    setCalendar(Calendar.getInstance(timeZone));
+  }
+
+  /**
+     Appends to <code>sbuf</code> the date in the format "dd MMM yyyy
+     HH:mm:ss,SSS" for example, "06 Nov 1994 08:49:37,459".
+
+     @param sbuf the string buffer to write to
+  */
+  public
+  StringBuffer format(Date date, StringBuffer sbuf,
+		      FieldPosition fieldPosition) {
+
+    calendar.setTime(date);
+
+    int day = calendar.get(Calendar.DAY_OF_MONTH);
+    if(day < 10)
+      sbuf.append('0');
+    sbuf.append(day);
+    sbuf.append(' ');
+    sbuf.append(shortMonths[calendar.get(Calendar.MONTH)]);
+    sbuf.append(' ');
+
+    int year =  calendar.get(Calendar.YEAR);
+    sbuf.append(year);
+    sbuf.append(' ');
+
+    return super.format(date, sbuf, fieldPosition);
+  }
+
+  /**
+     This method does not do anything but return <code>null</code>.
+   */
+  public
+  Date parse(java.lang.String s, ParsePosition pos) {
+    return null;
+  }
+}
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/FormattingInfo.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/FormattingInfo.java
new file mode 100644
index 0000000..b5d9dff
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/FormattingInfo.java
@@ -0,0 +1,47 @@
+/*
+ * 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.servicemix.kernel.gshell.log.layout;
+
+
+/**
+ * Copied from log4j
+ */
+/**
+   FormattingInfo instances contain the information obtained when parsing
+   formatting modifiers in conversion modifiers.
+
+   @author <a href=mailto:jim_cakalic@na.biomerieux.com>Jim Cakalic</a>
+   @author Ceki G&uuml;lc&uuml;
+
+   @since 0.8.2
+ */
+public class FormattingInfo {
+  int min = -1;
+  int max = 0x7FFFFFFF;
+  boolean leftAlign = false;
+
+  void reset() {
+    min = -1;
+    max = 0x7FFFFFFF;
+    leftAlign = false;
+  }
+
+  void dump() {
+    //LogLog.debug("min="+min+", max="+max+", leftAlign="+leftAlign);
+  }
+}
+
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/ISO8601DateFormat.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/ISO8601DateFormat.java
new file mode 100644
index 0000000..f19b8bd
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/ISO8601DateFormat.java
@@ -0,0 +1,155 @@
+/*
+ * 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.servicemix.kernel.gshell.log.layout;
+
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * Copied from log4j
+ */
+// Contributors: Arndt Schoenewald <arndt@ibm23093i821.mc.schoenewald.de>
+/**
+   Formats a {@link Date} in the format "yyyy-MM-dd HH:mm:ss,SSS" for example
+   "1999-11-27 15:49:37,459".
+
+   <p>Refer to the <a
+   href=http://www.cl.cam.ac.uk/~mgk25/iso-time.html>summary of the
+   International Standard Date and Time Notation</a> for more
+   information on this format.
+
+   @author Ceki G&uuml;lc&uuml;
+   @author Andrew Vajoczki
+
+   @since 0.7.5
+*/
+public class ISO8601DateFormat extends AbsoluteTimeDateFormat {
+
+  public
+  ISO8601DateFormat() {
+  }
+
+  public
+  ISO8601DateFormat(TimeZone timeZone) {
+    super(timeZone);
+  }
+
+  static private long   lastTime;
+  static private char[] lastTimeString = new char[20];
+
+  /**
+     Appends a date in the format "YYYY-mm-dd HH:mm:ss,SSS"
+     to <code>sbuf</code>. For example: "1999-11-27 15:49:37,459".
+
+     @param sbuf the <code>StringBuffer</code> to write to
+  */
+  public
+  StringBuffer format(Date date, StringBuffer sbuf,
+		      FieldPosition fieldPosition) {
+
+    long now = date.getTime();
+    int millis = (int)(now % 1000);
+
+    if ((now - millis) != lastTime) {
+      // We reach this point at most once per second
+      // across all threads instead of each time format()
+      // is called. This saves considerable CPU time.
+
+      calendar.setTime(date);
+
+      int start = sbuf.length();
+
+      int year =  calendar.get(Calendar.YEAR);
+      sbuf.append(year);
+
+      String month;
+      switch(calendar.get(Calendar.MONTH)) {
+      case Calendar.JANUARY: month = "-01-"; break;
+      case Calendar.FEBRUARY: month = "-02-";  break;
+      case Calendar.MARCH: month = "-03-"; break;
+      case Calendar.APRIL: month = "-04-";  break;
+      case Calendar.MAY: month = "-05-"; break;
+      case Calendar.JUNE: month = "-06-";  break;
+      case Calendar.JULY: month = "-07-"; break;
+      case Calendar.AUGUST: month = "-08-";  break;
+      case Calendar.SEPTEMBER: month = "-09-"; break;
+      case Calendar.OCTOBER: month = "-10-"; break;
+      case Calendar.NOVEMBER: month = "-11-";  break;
+      case Calendar.DECEMBER: month = "-12-";  break;
+      default: month = "-NA-"; break;
+      }
+      sbuf.append(month);
+
+      int day = calendar.get(Calendar.DAY_OF_MONTH);
+      if(day < 10)
+	sbuf.append('0');
+      sbuf.append(day);
+
+      sbuf.append(' ');
+
+      int hour = calendar.get(Calendar.HOUR_OF_DAY);
+      if(hour < 10) {
+	sbuf.append('0');
+      }
+      sbuf.append(hour);
+      sbuf.append(':');
+
+      int mins = calendar.get(Calendar.MINUTE);
+      if(mins < 10) {
+	sbuf.append('0');
+      }
+      sbuf.append(mins);
+      sbuf.append(':');
+
+      int secs = calendar.get(Calendar.SECOND);
+      if(secs < 10) {
+	sbuf.append('0');
+      }
+      sbuf.append(secs);
+
+      sbuf.append(',');
+
+      // store the time string for next time to avoid recomputation
+      sbuf.getChars(start, sbuf.length(), lastTimeString, 0);
+      lastTime = now - millis;
+    }
+    else {
+      sbuf.append(lastTimeString);
+    }
+
+
+    if (millis < 100)
+      sbuf.append('0');
+    if (millis < 10)
+      sbuf.append('0');
+
+    sbuf.append(millis);
+    return sbuf;
+  }
+
+  /**
+    This method does not do anything but return <code>null</code>.
+   */
+  public
+  Date parse(java.lang.String s, ParsePosition pos) {
+    return null;
+  }
+}
+
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/PatternConverter.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/PatternConverter.java
new file mode 100644
index 0000000..1652d57
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/PatternConverter.java
@@ -0,0 +1,110 @@
+/*
+ * 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.servicemix.kernel.gshell.log.layout;
+
+import org.ops4j.pax.logging.spi.PaxLoggingEvent;
+
+/**
+
+   <p>PatternConverter is an abtract class that provides the
+   formatting functionality that derived classes need.
+
+   <p>Conversion specifiers in a conversion patterns are parsed to
+   individual PatternConverters. Each of which is responsible for
+   converting a logging event in a converter specific manner.
+
+   @author <a href="mailto:cakalijp@Maritz.com">James P. Cakalic</a>
+   @author Ceki G&uuml;lc&uuml;
+
+   @since 0.8.2
+ */
+public abstract class PatternConverter {
+  public PatternConverter next;
+  int min = -1;
+  int max = 0x7FFFFFFF;
+  boolean leftAlign = false;
+
+  protected
+  PatternConverter() {  }
+
+  protected
+  PatternConverter(FormattingInfo fi) {
+    min = fi.min;
+    max = fi.max;
+    leftAlign = fi.leftAlign;
+  }
+
+  /**
+     Derived pattern converters must override this method in order to
+     convert conversion specifiers in the correct way.
+  */
+  abstract
+  protected
+  String convert(PaxLoggingEvent event);
+
+  /**
+     A template method for formatting in a converter specific way.
+   */
+  public
+  void format(StringBuffer sbuf, PaxLoggingEvent e) {
+    String s = convert(e);
+
+    if(s == null) {
+      if(0 < min)
+	spacePad(sbuf, min);
+      return;
+    }
+
+    int len = s.length();
+
+    if(len > max)
+      sbuf.append(s.substring(len-max));
+    else if(len < min) {
+      if(leftAlign) {
+	sbuf.append(s);
+	spacePad(sbuf, min-len);
+      }
+      else {
+	spacePad(sbuf, min-len);
+	sbuf.append(s);
+      }
+    }
+    else
+      sbuf.append(s);
+  }
+
+  static String[] SPACES = {" ", "  ", "    ", "        ", //1,2,4,8 spaces
+			    "                ", // 16 spaces
+			    "                                " }; // 32 spaces
+
+  /**
+     Fast space padding method.
+  */
+  public
+  void spacePad(StringBuffer sbuf, int length) {
+    while(length >= 32) {
+      sbuf.append(SPACES[5]);
+      length -= 32;
+    }
+
+    for(int i = 4; i >= 0; i--) {
+      if((length & (1<<i)) != 0) {
+	sbuf.append(SPACES[i]);
+      }
+    }
+  }
+}
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/PatternParser.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/PatternParser.java
new file mode 100644
index 0000000..4a74fd1
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/PatternParser.java
@@ -0,0 +1,527 @@
+/*
+ * 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.servicemix.kernel.gshell.log.layout;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.apache.log4j.spi.LoggingEvent;
+import org.ops4j.pax.logging.spi.PaxLocationInfo;
+import org.ops4j.pax.logging.spi.PaxLoggingEvent;
+
+/**
+ * Copied from log4j
+ */
+// Contributors:   Nelson Minar <(nelson@monkey.org>
+//                 Igor E. Poteryaev <jah@mail.ru>
+//                 Reinhard Deschler <reinhard.deschler@web.de>
+/**
+   Most of the work of the {@link org.apache.log4j.PatternLayout} class
+   is delegated to the PatternParser class.
+
+   @author <a href=mailto:"cakalijp@Maritz.com">James P. Cakalic</a>
+   @author Ceki G&uuml;lc&uuml;
+   @author Anders Kristensen
+
+   @since 0.8.2
+*/
+public class PatternParser {
+
+  private static final String LINE_SEP = System.getProperty("line.separator");
+
+  private static final char ESCAPE_CHAR = '%';
+
+  private static final int LITERAL_STATE = 0;
+  private static final int CONVERTER_STATE = 1;
+  private static final int MINUS_STATE = 2;
+  private static final int DOT_STATE = 3;
+  private static final int MIN_STATE = 4;
+  private static final int MAX_STATE = 5;
+
+  static final int FULL_LOCATION_CONVERTER = 1000;
+  static final int METHOD_LOCATION_CONVERTER = 1001;
+  static final int CLASS_LOCATION_CONVERTER = 1002;
+  static final int LINE_LOCATION_CONVERTER = 1003;
+  static final int FILE_LOCATION_CONVERTER = 1004;
+
+  static final int RELATIVE_TIME_CONVERTER = 2000;
+  static final int THREAD_CONVERTER = 2001;
+  static final int LEVEL_CONVERTER = 2002;
+  static final int NDC_CONVERTER = 2003;
+  static final int MESSAGE_CONVERTER = 2004;
+
+  int state;
+  protected StringBuffer currentLiteral = new StringBuffer(32);
+  protected int patternLength;
+  protected int i;
+  PatternConverter head;
+  PatternConverter tail;
+  protected FormattingInfo formattingInfo = new FormattingInfo();
+  protected String pattern;
+
+  public
+  PatternParser(String pattern) {
+    this.pattern = pattern;
+    patternLength =  pattern.length();
+    state = LITERAL_STATE;
+  }
+
+  private
+  void  addToList(PatternConverter pc) {
+    if(head == null) {
+      head = tail = pc;
+    } else {
+      tail.next = pc;
+      tail = pc;
+    }
+  }
+
+  protected
+  String extractOption() {
+    if((i < patternLength) && (pattern.charAt(i) == '{')) {
+      int end = pattern.indexOf('}', i);
+      if (end > i) {
+	String r = pattern.substring(i + 1, end);
+	i = end+1;
+	return r;
+      }
+    }
+    return null;
+  }
+
+
+  /**
+     The option is expected to be in decimal and positive. In case of
+     error, zero is returned.  */
+  protected
+  int extractPrecisionOption() {
+    String opt = extractOption();
+    int r = 0;
+    if(opt != null) {
+      try {
+	r = Integer.parseInt(opt);
+	if(r <= 0) {
+	    //LogLog.error("Precision option (" + opt + ") isn't a positive integer.");
+	    r = 0;
+	}
+      }
+      catch (NumberFormatException e) {
+	//LogLog.error("Category option \""+opt+"\" not a decimal integer.", e);
+      }
+    }
+    return r;
+  }
+
+  public
+  PatternConverter parse() {
+    char c;
+    i = 0;
+    while(i < patternLength) {
+      c = pattern.charAt(i++);
+      switch(state) {
+      case LITERAL_STATE:
+        // In literal state, the last char is always a literal.
+        if(i == patternLength) {
+          currentLiteral.append(c);
+          continue;
+        }
+        if(c == ESCAPE_CHAR) {
+          // peek at the next char.
+          switch(pattern.charAt(i)) {
+          case ESCAPE_CHAR:
+            currentLiteral.append(c);
+            i++; // move pointer
+            break;
+          case 'n':
+            currentLiteral.append(LINE_SEP);
+            i++; // move pointer
+            break;
+          default:
+            if(currentLiteral.length() != 0) {
+              addToList(new LiteralPatternConverter(
+                                                  currentLiteral.toString()));
+              //LogLog.debug("Parsed LITERAL converter: \""
+              //           +currentLiteral+"\".");
+            }
+            currentLiteral.setLength(0);
+            currentLiteral.append(c); // append %
+            state = CONVERTER_STATE;
+            formattingInfo.reset();
+          }
+        }
+        else {
+          currentLiteral.append(c);
+        }
+        break;
+      case CONVERTER_STATE:
+	currentLiteral.append(c);
+	switch(c) {
+	case '-':
+	  formattingInfo.leftAlign = true;
+	  break;
+	case '.':
+	  state = DOT_STATE;
+	  break;
+	default:
+	  if(c >= '0' && c <= '9') {
+	    formattingInfo.min = c - '0';
+	    state = MIN_STATE;
+	  }
+	  else
+	    finalizeConverter(c);
+	} // switch
+	break;
+      case MIN_STATE:
+	currentLiteral.append(c);
+	if(c >= '0' && c <= '9')
+	  formattingInfo.min = formattingInfo.min*10 + (c - '0');
+	else if(c == '.')
+	  state = DOT_STATE;
+	else {
+	  finalizeConverter(c);
+	}
+	break;
+      case DOT_STATE:
+	currentLiteral.append(c);
+	if(c >= '0' && c <= '9') {
+	  formattingInfo.max = c - '0';
+	   state = MAX_STATE;
+	}
+	else {
+	  //LogLog.error("Error occured in position "+i+".\n Was expecting digit, instead got char \""+c+"\".");
+	  state = LITERAL_STATE;
+	}
+	break;
+      case MAX_STATE:
+	currentLiteral.append(c);
+	if(c >= '0' && c <= '9')
+	  formattingInfo.max = formattingInfo.max*10 + (c - '0');
+	else {
+	  finalizeConverter(c);
+	  state = LITERAL_STATE;
+	}
+	break;
+      } // switch
+    } // while
+    if(currentLiteral.length() != 0) {
+      addToList(new LiteralPatternConverter(currentLiteral.toString()));
+      //LogLog.debug("Parsed LITERAL converter: \""+currentLiteral+"\".");
+    }
+    return head;
+  }
+
+  protected
+  void finalizeConverter(char c) {
+    PatternConverter pc = null;
+    switch(c) {
+    case 'c':
+      pc = new CategoryPatternConverter(formattingInfo,
+					extractPrecisionOption());
+      //LogLog.debug("CATEGORY converter.");
+      //formattingInfo.dump();
+      currentLiteral.setLength(0);
+      break;
+    case 'C':
+      pc = new ClassNamePatternConverter(formattingInfo,
+					 extractPrecisionOption());
+      //LogLog.debug("CLASS_NAME converter.");
+      //formattingInfo.dump();
+      currentLiteral.setLength(0);
+      break;
+    case 'd':
+      String dateFormatStr = AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT;
+      DateFormat df;
+      String dOpt = extractOption();
+      if(dOpt != null)
+	dateFormatStr = dOpt;
+
+      if(dateFormatStr.equalsIgnoreCase(
+                                    AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT))
+	df = new  ISO8601DateFormat();
+      else if(dateFormatStr.equalsIgnoreCase(
+                                   AbsoluteTimeDateFormat.ABS_TIME_DATE_FORMAT))
+	df = new AbsoluteTimeDateFormat();
+      else if(dateFormatStr.equalsIgnoreCase(
+                              AbsoluteTimeDateFormat.DATE_AND_TIME_DATE_FORMAT))
+	df = new DateTimeDateFormat();
+      else {
+	try {
+	  df = new SimpleDateFormat(dateFormatStr);
+	}
+	catch (IllegalArgumentException e) {
+	  //LogLog.error("Could not instantiate SimpleDateFormat with " + dateFormatStr, e);
+	  df = new ISO8601DateFormat();
+	}
+      }
+      pc = new DatePatternConverter(formattingInfo, df);
+      //LogLog.debug("DATE converter {"+dateFormatStr+"}.");
+      //formattingInfo.dump();
+      currentLiteral.setLength(0);
+      break;
+    case 'F':
+      pc = new LocationPatternConverter(formattingInfo,
+					FILE_LOCATION_CONVERTER);
+      //LogLog.debug("File name converter.");
+      //formattingInfo.dump();
+      currentLiteral.setLength(0);
+      break;
+    /*case 'l':
+      pc = new LocationPatternConverter(formattingInfo,
+					FULL_LOCATION_CONVERTER);
+      //LogLog.debug("Location converter.");
+      //formattingInfo.dump();
+      currentLiteral.setLength(0);
+      break;*/
+    case 'L':
+      pc = new LocationPatternConverter(formattingInfo,
+					LINE_LOCATION_CONVERTER);
+      //LogLog.debug("LINE NUMBER converter.");
+      //formattingInfo.dump();
+      currentLiteral.setLength(0);
+      break;
+    case 'm':
+      pc = new BasicPatternConverter(formattingInfo, MESSAGE_CONVERTER);
+      //LogLog.debug("MESSAGE converter.");
+      //formattingInfo.dump();
+      currentLiteral.setLength(0);
+      break;
+    case 'M':
+      pc = new LocationPatternConverter(formattingInfo,
+					METHOD_LOCATION_CONVERTER);
+      //LogLog.debug("METHOD converter.");
+      //formattingInfo.dump();
+      currentLiteral.setLength(0);
+      break;
+    case 'p':
+      pc = new BasicPatternConverter(formattingInfo, LEVEL_CONVERTER);
+      //LogLog.debug("LEVEL converter.");
+      //formattingInfo.dump();
+      currentLiteral.setLength(0);
+      break;
+    case 'r':
+      pc = new BasicPatternConverter(formattingInfo,
+					 RELATIVE_TIME_CONVERTER);
+      //LogLog.debug("RELATIVE time converter.");
+      //formattingInfo.dump();
+      currentLiteral.setLength(0);
+      break;
+    case 't':
+      pc = new BasicPatternConverter(formattingInfo, THREAD_CONVERTER);
+      //LogLog.debug("THREAD converter.");
+      //formattingInfo.dump();
+      currentLiteral.setLength(0);
+      break;
+      /*case 'u':
+      if(i < patternLength) {
+	char cNext = pattern.charAt(i);
+	if(cNext >= '0' && cNext <= '9') {
+	  pc = new UserFieldPatternConverter(formattingInfo, cNext - '0');
+	  LogLog.debug("USER converter ["+cNext+"].");
+	  formattingInfo.dump();
+	  currentLiteral.setLength(0);
+	  i++;
+	}
+	else
+	  LogLog.error("Unexpected char" +cNext+" at position "+i);
+      }
+      break;*/
+    /*case 'x':
+      pc = new BasicPatternConverter(formattingInfo, NDC_CONVERTER);
+      //LogLog.debug("NDC converter.");
+      currentLiteral.setLength(0);
+      break;
+    case 'X':
+      String xOpt = extractOption();
+      pc = new MDCPatternConverter(formattingInfo, xOpt);
+      currentLiteral.setLength(0);
+      break;*/
+    default:
+      //LogLog.error("Unexpected char [" +c+"] at position "+i+" in conversion patterrn.");
+      pc = new LiteralPatternConverter(currentLiteral.toString());
+      currentLiteral.setLength(0);
+    }
+
+    addConverter(pc);
+  }
+
+  protected
+  void addConverter(PatternConverter pc) {
+    currentLiteral.setLength(0);
+    // Add the pattern converter to the list.
+    addToList(pc);
+    // Next pattern is assumed to be a literal.
+    state = LITERAL_STATE;
+    // Reset formatting info
+    formattingInfo.reset();
+  }
+
+  // ---------------------------------------------------------------------
+  //                      PatternConverters
+  // ---------------------------------------------------------------------
+
+  private static class BasicPatternConverter extends PatternConverter {
+    int type;
+
+    BasicPatternConverter(FormattingInfo formattingInfo, int type) {
+      super(formattingInfo);
+      this.type = type;
+    }
+
+    public
+    String convert(PaxLoggingEvent event) {
+      switch(type) {
+      case RELATIVE_TIME_CONVERTER:
+	return (Long.toString(event.getTimeStamp() - LoggingEvent.getStartTime()));
+      case THREAD_CONVERTER:
+	return event.getThreadName();
+      case LEVEL_CONVERTER:
+	return event.getLevel().toString();
+    //  case NDC_CONVERTER:
+	//return event.getNDC();
+      case MESSAGE_CONVERTER: {
+	return event.getRenderedMessage();
+      }
+      default: return null;
+      }
+    }
+  }
+
+  private static class LiteralPatternConverter extends PatternConverter {
+    private String literal;
+
+    LiteralPatternConverter(String value) {
+      literal = value;
+    }
+
+    public
+    final
+    void format(StringBuffer sbuf, LoggingEvent event) {
+      sbuf.append(literal);
+    }
+
+    public
+    String convert(PaxLoggingEvent event) {
+      return literal;
+    }
+  }
+
+  private static class DatePatternConverter extends PatternConverter {
+    private DateFormat df;
+    private Date date;
+
+    DatePatternConverter(FormattingInfo formattingInfo, DateFormat df) {
+      super(formattingInfo);
+      date = new Date();
+      this.df = df;
+    }
+
+    public
+    String convert(PaxLoggingEvent event) {
+      date.setTime(event.getTimeStamp());
+      String converted = null;
+      try {
+        converted = df.format(date);
+      }
+      catch (Exception ex) {
+        //LogLog.error("Error occured while converting date.", ex);
+      }
+      return converted;
+    }
+  }
+
+  private class LocationPatternConverter extends PatternConverter {
+    int type;
+
+    LocationPatternConverter(FormattingInfo formattingInfo, int type) {
+      super(formattingInfo);
+      this.type = type;
+    }
+
+    public
+    String convert(PaxLoggingEvent event) {
+      PaxLocationInfo locationInfo = event.getLocationInformation();
+      switch(type) {
+      /*case FULL_LOCATION_CONVERTER:
+	return locationInfo.fullInfo;*/
+      case METHOD_LOCATION_CONVERTER:
+	return locationInfo.getMethodName();
+      case LINE_LOCATION_CONVERTER:
+	return locationInfo.getLineNumber();
+      case FILE_LOCATION_CONVERTER:
+	return locationInfo.getFileName();
+      default: return null;
+      }
+    }
+  }
+
+  private static abstract class NamedPatternConverter extends PatternConverter {
+    int precision;
+
+    NamedPatternConverter(FormattingInfo formattingInfo, int precision) {
+      super(formattingInfo);
+      this.precision =  precision;
+    }
+
+    abstract
+    String getFullyQualifiedName(PaxLoggingEvent event);
+
+    public
+    String convert(PaxLoggingEvent event) {
+      String n = getFullyQualifiedName(event);
+      if(precision <= 0)
+	return n;
+      else {
+	int len = n.length();
+
+	// We substract 1 from 'len' when assigning to 'end' to avoid out of
+	// bounds exception in return r.substring(end+1, len). This can happen if
+	// precision is 1 and the category name ends with a dot.
+	int end = len -1 ;
+	for(int i = precision; i > 0; i--) {
+	  end = n.lastIndexOf('.', end-1);
+	  if(end == -1)
+	    return n;
+	}
+	return n.substring(end+1, len);
+      }
+    }
+  }
+
+  private class ClassNamePatternConverter extends NamedPatternConverter {
+
+    ClassNamePatternConverter(FormattingInfo formattingInfo, int precision) {
+      super(formattingInfo, precision);
+    }
+
+    String getFullyQualifiedName(PaxLoggingEvent event) {
+      return event.getLocationInformation().getClassName();
+    }
+  }
+
+  private class CategoryPatternConverter extends NamedPatternConverter {
+
+    CategoryPatternConverter(FormattingInfo formattingInfo, int precision) {
+      super(formattingInfo, precision);
+    }
+
+    String getFullyQualifiedName(PaxLoggingEvent event) {
+      return event.getLoggerName();
+    }
+  }
+}
+
diff --git a/karaf/gshell/gshell-log/src/main/resources/META-INF/spring/gshell-log.xml b/karaf/gshell/gshell-log/src/main/resources/META-INF/spring/gshell-log.xml
new file mode 100644
index 0000000..2055fe4
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/resources/META-INF/spring/gshell-log.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:ctx="http://www.springframework.org/schema/context"
+       xmlns:osgi="http://www.springframework.org/schema/osgi"
+       xmlns:osgix="http://www.springframework.org/schema/osgi-compendium"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+       xsi:schemaLocation="
+  http://www.springframework.org/schema/beans
+  http://www.springframework.org/schema/beans/spring-beans.xsd
+  http://www.springframework.org/schema/context
+  http://www.springframework.org/schema/context/spring-context.xsd
+  http://www.springframework.org/schema/osgi
+  http://www.springframework.org/schema/osgi/spring-osgi.xsd
+  http://www.springframework.org/schema/osgi-compendium
+  http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd
+  http://www.springframework.org/schema/util
+  http://www.springframework.org/schema/util/spring-util.xsd
+  http://servicemix.apache.org/schema/servicemix-gshell
+  http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+    <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+    <gshell:command-bundle>
+        <gshell:command name="log/display">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.log.DisplayLog">
+                <property name="events" ref="events" />
+                <property name="pattern" value="${pattern}" />
+            </gshell:action>
+        </gshell:command>
+        <gshell:link name="log/d" target="log/display" />
+        <gshell:command name="log/display-exception">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.log.DisplayException">
+                <property name="events" ref="events" />
+            </gshell:action>
+        </gshell:command>
+        <gshell:link name="log/de" target="log/display-exception" />
+        <gshell:command name="log/get">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.log.GetLogLevel" />
+        </gshell:command>
+        <gshell:command name="log/set">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.log.SetLogLevel" />
+        </gshell:command>
+
+        <gshell:alias name="ld" alias="log/d" />
+        <gshell:alias name="lde" alias="log/de" />
+    </gshell:command-bundle>
+
+    <bean id="vmLogAppender" class="org.apache.servicemix.kernel.gshell.log.VmLogAppender">
+        <property name="events" ref="events" />
+    </bean>
+
+    <bean id="events" class="org.apache.servicemix.kernel.gshell.log.LruList">
+        <constructor-arg value="${size}" />
+    </bean>
+
+    <osgi:service ref="vmLogAppender" interface="org.ops4j.pax.logging.spi.PaxAppender">
+        <osgi:service-properties>
+            <entry>
+                <key><util:constant static-field="org.ops4j.pax.logging.PaxLoggingService.APPENDER_NAME_PROPERTY"/></key>
+                <value>VmLogAppender</value>
+            </entry>
+        </osgi:service-properties>
+    </osgi:service>
+
+    <osgix:cm-properties id="cmProps" persistent-id="org.apache.servicemix.log">
+        <prop key="size">500</prop>
+        <prop key="pattern">%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n</prop>
+    </osgix:cm-properties>
+
+    <ctx:property-placeholder properties-ref="cmProps" />
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/DisplayException.properties b/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/DisplayException.properties
new file mode 100644
index 0000000..ff84091
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/DisplayException.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Display the last exception from the log.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/DisplayLog.properties b/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/DisplayLog.properties
new file mode 100644
index 0000000..d360b1a
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/DisplayLog.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Display log entries.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/GetLogLevel.properties b/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/GetLogLevel.properties
new file mode 100644
index 0000000..94ee1d3
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/GetLogLevel.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Show log level.
+
+command.manual=\
+  TODO: date manual
diff --git a/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/SetLogLevel.properties b/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/SetLogLevel.properties
new file mode 100644
index 0000000..511734b
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/SetLogLevel.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Set log level.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/pom.xml b/karaf/gshell/gshell-obr/pom.xml
new file mode 100644
index 0000000..58cc9e8
--- /dev/null
+++ b/karaf/gshell/gshell-obr/pom.xml
@@ -0,0 +1,85 @@
+<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">
+
+    <!--
+
+        Licensed to the Apache Software Foundation (ASF) under one or more
+        contributor license agreements.  See the NOTICE file distributed with
+        this work for additional information regarding copyright ownership.
+        The ASF licenses this file to You under the Apache License, Version 2.0
+        (the "License"); you may not use this file except in compliance with
+        the License.  You may obtain a copy of the License at
+
+           http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.servicemix.kernel.gshell</groupId>
+        <artifactId>gshell</artifactId>
+        <version>1.2.0-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.apache.servicemix.kernel.gshell</groupId>
+    <artifactId>org.apache.servicemix.kernel.gshell.obr</artifactId>
+    <packaging>bundle</packaging>
+    <version>1.2.0-SNAPSHOT</version>
+    <name>Apache ServiceMix Kernel :: GShell OBR Commands</name>
+
+    <description>
+        Provides the OBR GShell commands
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.bundlerepository</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.servicemix.kernel.gshell</groupId>
+            <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.osgi</groupId>
+            <artifactId>spring-osgi-core</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+                        <Export-Package>org.apache.servicemix.kernel.gshell.obr*;version=${project.version}</Export-Package>
+                        <Import-Package>
+                            org.apache.geronimo.gshell.wisdom.command,
+                            org.apache.geronimo.gshell.wisdom.registry,
+                            org.apache.servicemix.kernel.gshell.core,
+                            *
+                        </Import-Package>
+                        <Private-Package>!*</Private-Package>
+                        <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/AddUrlCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/AddUrlCommand.java
new file mode 100644
index 0000000..d416674
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/AddUrlCommand.java
@@ -0,0 +1,35 @@
+/*
+ * 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.servicemix.kernel.gshell.obr;
+
+import java.net.URL;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.service.obr.RepositoryAdmin;
+
+public class AddUrlCommand extends ObrCommandSupport {
+
+    @Argument(required = true, multiValued = true, description = "Repository URLs")
+    List<String> urls;
+
+    protected void doExecute(RepositoryAdmin admin) throws Exception {
+        for (String url : urls) {
+            admin.addRepository(new URL(url));
+        }
+    }
+}
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/DeployCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/DeployCommand.java
new file mode 100644
index 0000000..7bbd1dc
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/DeployCommand.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.obr;
+
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.service.obr.RepositoryAdmin;
+
+public class DeployCommand extends ObrCommandSupport {
+
+    @Argument(required = true, multiValued = true, description = "List of bundles")
+    protected List<String> bundles;
+
+    protected void doExecute(RepositoryAdmin admin) throws Exception {
+        doDeploy(admin, bundles, false);
+    }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/FileUtil.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/FileUtil.java
new file mode 100644
index 0000000..344e592
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/FileUtil.java
@@ -0,0 +1,177 @@
+/*
+ * 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.servicemix.kernel.gshell.obr;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+
+public class FileUtil
+{
+    public static void downloadSource(
+        PrintWriter out, PrintWriter err,
+        URL srcURL, String dirStr, boolean extract)
+    {
+        // Get the file name from the URL.
+        String fileName = (srcURL.getFile().lastIndexOf('/') > 0)
+            ? srcURL.getFile().substring(srcURL.getFile().lastIndexOf('/') + 1)
+            : srcURL.getFile();
+
+        try
+        {
+            out.println("Connecting...");
+
+            File dir = new File(dirStr);
+            if (!dir.exists())
+            {
+                err.println("Destination directory does not exist.");
+            }
+            File file = new File(dir, fileName);
+
+            OutputStream os = new FileOutputStream(file);
+            URLConnection conn = srcURL.openConnection();
+            int total = conn.getContentLength();
+            InputStream is = conn.getInputStream();
+
+            if (total > 0)
+            {
+                out.println("Downloading " + fileName
+                    + " ( " + total + " bytes ).");
+            }
+            else
+            {
+                out.println("Downloading " + fileName + ".");
+            }
+            byte[] buffer = new byte[4096];
+            int count = 0;
+            for (int len = is.read(buffer); len > 0; len = is.read(buffer))
+            {
+                count += len;
+                os.write(buffer, 0, len);
+            }
+
+            os.close();
+            is.close();
+
+            if (extract)
+            {
+                is = new FileInputStream(file);
+                JarInputStream jis = new JarInputStream(is);
+                out.println("Extracting...");
+                unjar(jis, dir);
+                jis.close();
+                file.delete();
+            }
+        }
+        catch (Exception ex)
+        {
+            err.println(ex);
+        }
+    }
+
+    public static void unjar(JarInputStream jis, File dir)
+        throws IOException
+    {
+        // Reusable buffer.
+        byte[] buffer = new byte[4096];
+
+        // Loop through JAR entries.
+        for (JarEntry je = jis.getNextJarEntry();
+             je != null;
+             je = jis.getNextJarEntry())
+        {
+            if (je.getName().startsWith("/"))
+            {
+                throw new IOException("JAR resource cannot contain absolute paths.");
+            }
+
+            File target = new File(dir, je.getName());
+
+            // Check to see if the JAR entry is a directory.
+            if (je.isDirectory())
+            {
+                if (!target.exists())
+                {
+                    if (!target.mkdirs())
+                    {
+                        throw new IOException("Unable to create target directory: "
+                            + target);
+                    }
+                }
+                // Just continue since directories do not have content to copy.
+                continue;
+            }
+
+            int lastIndex = je.getName().lastIndexOf('/');
+            String name = (lastIndex >= 0) ?
+                je.getName().substring(lastIndex + 1) : je.getName();
+            String destination = (lastIndex >= 0) ?
+                je.getName().substring(0, lastIndex) : "";
+
+            // JAR files use '/', so convert it to platform separator.
+            destination = destination.replace('/', File.separatorChar);
+            copy(jis, dir, name, destination, buffer);
+        }
+    }
+
+    public static void copy(
+        InputStream is, File dir, String destName, String destDir, byte[] buffer)
+        throws IOException
+    {
+        if (destDir == null)
+        {
+            destDir = "";
+        }
+
+        // Make sure the target directory exists and
+        // that is actually a directory.
+        File targetDir = new File(dir, destDir);
+        if (!targetDir.exists())
+        {
+            if (!targetDir.mkdirs())
+            {
+                throw new IOException("Unable to create target directory: "
+                    + targetDir);
+            }
+        }
+        else if (!targetDir.isDirectory())
+        {
+            throw new IOException("Target is not a directory: "
+                + targetDir);
+        }
+
+        BufferedOutputStream bos = new BufferedOutputStream(
+            new FileOutputStream(new File(targetDir, destName)));
+        int count = 0;
+        while ((count = is.read(buffer)) > 0)
+        {
+            bos.write(buffer, 0, count);
+        }
+        bos.close();
+    }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/InfoCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/InfoCommand.java
new file mode 100644
index 0000000..50ede86
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/InfoCommand.java
@@ -0,0 +1,104 @@
+/*
+ * 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.servicemix.kernel.gshell.obr;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Array;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.service.obr.Capability;
+import org.osgi.service.obr.RepositoryAdmin;
+import org.osgi.service.obr.Requirement;
+import org.osgi.service.obr.Resource;
+
+public class InfoCommand extends ObrCommandSupport {
+
+    @Argument(required = true, multiValued = true)
+    List<String> bundles;
+
+    protected void doExecute(RepositoryAdmin admin) throws Exception {
+        for (String bundle : bundles) {
+            String[] target = getTarget(bundle);
+            Resource[] resources = searchRepository(admin, target[0], target[1]);
+            if (resources == null)
+            {
+                io.err.println("Unknown bundle and/or version: "
+                    + target[0]);
+            }
+            else
+            {
+                for (int resIdx = 0; resIdx < resources.length; resIdx++)
+                {
+                    if (resIdx > 0)
+                    {
+                        io.out.println("");
+                    }
+                    printResource(io.out, resources[resIdx]);
+                }
+            }
+        }
+    }
+
+    private void printResource(PrintWriter out, Resource resource)
+    {
+        printUnderline(out, resource.getPresentationName().length());
+        out.println(resource.getPresentationName());
+        printUnderline(out, resource.getPresentationName().length());
+
+        Map map = resource.getProperties();
+        for (Iterator iter = map.entrySet().iterator(); iter.hasNext(); )
+        {
+            Map.Entry entry = (Map.Entry) iter.next();
+            if (entry.getValue().getClass().isArray())
+            {
+                out.println(entry.getKey() + ":");
+                for (int j = 0; j < Array.getLength(entry.getValue()); j++)
+                {
+                    out.println("   " + Array.get(entry.getValue(), j));
+                }
+            }
+            else
+            {
+                out.println(entry.getKey() + ": " + entry.getValue());
+            }
+        }
+
+        Requirement[] reqs = resource.getRequirements();
+        if ((reqs != null) && (reqs.length > 0))
+        {
+            out.println("Requires:");
+            for (int i = 0; i < reqs.length; i++)
+            {
+                out.println("   " + reqs[i].getFilter());
+            }
+        }
+
+        Capability[] caps = resource.getCapabilities();
+        if ((caps != null) && (caps.length > 0))
+        {
+            out.println("Capabilities:");
+            for (int i = 0; i < caps.length; i++)
+            {
+                out.println("   " + caps[i].getProperties());
+            }
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ListCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ListCommand.java
new file mode 100644
index 0000000..37ff08f
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ListCommand.java
@@ -0,0 +1,85 @@
+/*
+ * 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.servicemix.kernel.gshell.obr;
+
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.framework.Version;
+import org.osgi.service.obr.RepositoryAdmin;
+import org.osgi.service.obr.Resource;
+
+public class ListCommand extends ObrCommandSupport {
+
+    @Argument(required = false, multiValued = true)
+    List<String> args;
+
+    protected void doExecute(RepositoryAdmin admin) throws Exception {
+        String substr = null;
+
+        if (args != null) {
+            for (String arg : args)
+            {
+                // Add a space in between tokens.
+                if (substr == null)
+                {
+                    substr = "";
+                }
+                else
+                {
+                    substr += " ";
+                }
+
+                substr += arg;
+            }
+        }
+        
+        StringBuffer sb = new StringBuffer();
+        if ((substr == null) || (substr.length() == 0))
+        {
+            sb.append("(|(presentationname=*)(symbolicname=*))");
+        }
+        else
+        {
+            sb.append("(|(presentationname=*");
+            sb.append(substr);
+            sb.append("*)(symbolicname=*");
+            sb.append(substr);
+            sb.append("*))");
+        }
+        Resource[] resources = admin.discoverResources(sb.toString());
+        for (int resIdx = 0; (resources != null) && (resIdx < resources.length); resIdx++)
+        {
+            String name = resources[resIdx].getPresentationName();
+            Version version = resources[resIdx].getVersion();
+            if (version != null)
+            {
+                io.out.println(name + " (" + version + ")");
+            }
+            else
+            {
+                io.out.println(name);
+            }
+        }
+
+        if (resources == null)
+        {
+            io.out.println("No matching bundles.");
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ListUrlCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ListUrlCommand.java
new file mode 100644
index 0000000..ae8eaa2
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ListUrlCommand.java
@@ -0,0 +1,35 @@
+/*
+ * 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.servicemix.kernel.gshell.obr;
+
+import org.osgi.service.obr.Repository;
+import org.osgi.service.obr.RepositoryAdmin;
+
+public class ListUrlCommand extends ObrCommandSupport {
+
+    protected void doExecute(RepositoryAdmin admin) {
+        Repository[] repos = admin.listRepositories();
+        if ((repos != null) && (repos.length > 0)) {
+            for (int i = 0; i < repos.length; i++) {
+                io.out.println(repos[i].getURL());
+            }
+        } else {
+            io.out.println("No repository URLs are set.");
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ObrCommandSupport.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ObrCommandSupport.java
new file mode 100644
index 0000000..dd6af1a
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ObrCommandSupport.java
@@ -0,0 +1,221 @@
+/*
+ * 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.servicemix.kernel.gshell.obr;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.Version;
+import org.osgi.service.obr.RepositoryAdmin;
+import org.osgi.service.obr.Requirement;
+import org.osgi.service.obr.Resolver;
+import org.osgi.service.obr.Resource;
+
+public abstract class ObrCommandSupport extends OsgiCommandSupport {
+
+    protected static final char VERSION_DELIM = ',';
+
+    protected Object doExecute() throws Exception {
+        // Get repository admin service.
+        ServiceReference ref = getBundleContext().getServiceReference(RepositoryAdmin.class.getName());
+        if (ref == null) {
+            io.out.println("RepositoryAdmin service is unavailable.");
+            return null;
+        }
+        try {
+            RepositoryAdmin admin = (RepositoryAdmin) getBundleContext().getService(ref);
+            if (admin == null) {
+                io.out.println("RepositoryAdmin service is unavailable.");
+                return null;
+            }
+
+            doExecute(admin);
+        }
+        finally {
+            getBundleContext().ungetService(ref);
+        }
+        return null;
+    }
+
+    protected abstract void doExecute(RepositoryAdmin admin) throws Exception;
+
+    protected Resource[] searchRepository(RepositoryAdmin admin, String targetId, String targetVersion)
+    {
+        // Try to see if the targetId is a bundle ID.
+        try
+        {
+            Bundle bundle = getBundleContext().getBundle(Long.parseLong(targetId));
+            targetId = bundle.getSymbolicName();
+        }
+        catch (NumberFormatException ex)
+        {
+            // It was not a number, so ignore.
+        }
+
+        // The targetId may be a bundle name or a bundle symbolic name,
+        // so create the appropriate LDAP query.
+        StringBuffer sb = new StringBuffer("(|(presentationname=");
+        sb.append(targetId);
+        sb.append(")(symbolicname=");
+        sb.append(targetId);
+        sb.append("))");
+        if (targetVersion != null)
+        {
+            sb.insert(0, "(&");
+            sb.append("(version=");
+            sb.append(targetVersion);
+            sb.append("))");
+        }
+        return admin.discoverResources(sb.toString());
+    }
+
+    public Resource selectNewestVersion(Resource[] resources)
+    {
+        int idx = -1;
+        Version v = null;
+        for (int i = 0; (resources != null) && (i < resources.length); i++)
+        {
+            if (i == 0)
+            {
+                idx = 0;
+                v = resources[i].getVersion();
+            }
+            else
+            {
+                Version vtmp = resources[i].getVersion();
+                if (vtmp.compareTo(v) > 0)
+                {
+                    idx = i;
+                    v = vtmp;
+                }
+            }
+        }
+        return (idx < 0) ? null : resources[idx];
+    }
+
+    protected String[] getTarget(String bundle) {
+        String[] target;
+        int idx = bundle.indexOf(VERSION_DELIM);
+        if (idx > 0) {
+            target = new String[] { bundle.substring(0, idx), bundle.substring(idx) };
+        }
+        else
+        {
+            target = new String[] { bundle, null };
+        }
+        return target;
+    }
+
+    protected void printUnderline(PrintWriter out, int length)
+    {
+        for (int i = 0; i < length; i++)
+        {
+            out.print('-');
+        }
+        out.println("");
+    }
+
+    protected void doDeploy(RepositoryAdmin admin, List<String> bundles, boolean start) throws Exception {
+        Resolver resolver = admin.resolver();
+        for (String bundle : bundles) {
+            String[] target = getTarget(bundle);
+            Resource resource = selectNewestVersion(searchRepository(admin, target[0], target[1]));
+            if (resource != null)
+            {
+                resolver.add(resource);
+            }
+            else
+            {
+                io.err.println("Unknown bundle - " + target[0]);
+            }
+        }
+        if ((resolver.getAddedResources() != null) &&
+            (resolver.getAddedResources().length > 0))
+        {
+            if (resolver.resolve())
+            {
+                io.out.println("Target resource(s):");
+                printUnderline(io.out, 19);
+                Resource[] resources = resolver.getAddedResources();
+                for (int resIdx = 0; (resources != null) && (resIdx < resources.length); resIdx++)
+                {
+                    io.out.println("   " + resources[resIdx].getPresentationName()
+                        + " (" + resources[resIdx].getVersion() + ")");
+                }
+                resources = resolver.getRequiredResources();
+                if ((resources != null) && (resources.length > 0))
+                {
+                    io.out.println("\nRequired resource(s):");
+                    printUnderline(io.out, 21);
+                    for (int resIdx = 0; resIdx < resources.length; resIdx++)
+                    {
+                        io.out.println("   " + resources[resIdx].getPresentationName()
+                            + " (" + resources[resIdx].getVersion() + ")");
+                    }
+                }
+                resources = resolver.getOptionalResources();
+                if ((resources != null) && (resources.length > 0))
+                {
+                    io.out.println("\nOptional resource(s):");
+                    printUnderline(io.out, 21);
+                    for (int resIdx = 0; resIdx < resources.length; resIdx++)
+                    {
+                        io.out.println("   " + resources[resIdx].getPresentationName()
+                            + " (" + resources[resIdx].getVersion() + ")");
+                    }
+                }
+
+                try
+                {
+                    io.out.print("\nDeploying...");
+                    resolver.deploy(start);
+                    io.out.println("done.");
+                }
+                catch (IllegalStateException ex)
+                {
+                    io.err.println(ex);
+                }
+            }
+            else
+            {
+                Requirement[] reqs = resolver.getUnsatisfiedRequirements();
+                if ((reqs != null) && (reqs.length > 0))
+                {
+                    io.out.println("Unsatisfied requirement(s):");
+                    printUnderline(io.out, 27);
+                    for (int reqIdx = 0; reqIdx < reqs.length; reqIdx++)
+                    {
+                        io.out.println("   " + reqs[reqIdx].getFilter());
+                        Resource[] resources = resolver.getResources(reqs[reqIdx]);
+                        for (int resIdx = 0; resIdx < resources.length; resIdx++)
+                        {
+                            io.out.println("      " + resources[resIdx].getPresentationName());
+                        }
+                    }
+                }
+                else
+                {
+                    io.out.println("Could not resolve targets.");
+                }
+            }
+        }
+
+    }
+}
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/RefreshUrlCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/RefreshUrlCommand.java
new file mode 100644
index 0000000..056d094
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/RefreshUrlCommand.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.obr;
+
+import java.net.URL;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.service.obr.Repository;
+import org.osgi.service.obr.RepositoryAdmin;
+
+public class RefreshUrlCommand extends ObrCommandSupport {
+
+    @Argument(required = false, multiValued = true, description = "Repository URLs (leave empty for all)")
+    List<String> urls;
+
+
+    protected void doExecute(RepositoryAdmin admin) throws Exception {
+		if (urls != null || urls.isEmpty()) {
+			for (String url : urls) {
+				admin.removeRepository(new URL(url));
+				admin.addRepository(new URL(url));
+			}
+		} else {
+			Repository[] repos = admin.listRepositories();
+			if ((repos != null) && (repos.length > 0)) {
+				for (int i = 0; i < repos.length; i++) {
+					admin.removeRepository(repos[i].getURL());
+					admin.addRepository(repos[i].getURL());
+				}
+			}
+		}
+    }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/RemoveUrlCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/RemoveUrlCommand.java
new file mode 100644
index 0000000..d122706
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/RemoveUrlCommand.java
@@ -0,0 +1,35 @@
+/*
+ * 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.servicemix.kernel.gshell.obr;
+
+import java.net.URL;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.service.obr.RepositoryAdmin;
+
+public class RemoveUrlCommand extends ObrCommandSupport {
+
+    @Argument(required = true, multiValued = true, description = "Repository URLs")
+    List<String> urls;
+
+    protected void doExecute(RepositoryAdmin admin) throws Exception {
+        for (String url : urls) {
+            admin.removeRepository(new URL(url));
+        }
+    }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/SourceCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/SourceCommand.java
new file mode 100644
index 0000000..f15db46
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/SourceCommand.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.obr;
+
+import java.net.URL;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.osgi.service.obr.RepositoryAdmin;
+import org.osgi.service.obr.Resource;
+
+public class SourceCommand extends ObrCommandSupport {
+
+    @Option(name = "-x", description = "Extract")
+    boolean extract;
+
+    @Argument(required = true, index = 0, description = "Local directory")
+    String localDir;
+
+    @Argument(required = true, index = 1, multiValued = true, description = "List of bundles")
+    List<String> bundles;
+
+    protected void doExecute(RepositoryAdmin admin) throws Exception {
+        for (String bundle : bundles) {
+            String[] target = getTarget(bundle);
+            Resource resource = selectNewestVersion(searchRepository(admin, target[0], target[1]));
+            if (resource == null)
+            {
+                io.err.println("Unknown bundle and/or version: " + target[0]);
+            }
+            else
+            {
+                URL srcURL = (URL) resource.getProperties().get(Resource.SOURCE_URL);
+                if (srcURL != null)
+                {
+                    FileUtil.downloadSource(io.out, io.err, srcURL, localDir, extract);
+                }
+                else
+                {
+                    io.err.println("Missing source URL: " + target[0]);
+                }
+            }
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/StartCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/StartCommand.java
new file mode 100644
index 0000000..1a34e26
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/StartCommand.java
@@ -0,0 +1,27 @@
+/*
+ * 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.servicemix.kernel.gshell.obr;
+
+import org.osgi.service.obr.RepositoryAdmin;
+
+public class StartCommand extends DeployCommand {
+
+    protected void doExecute(RepositoryAdmin admin) throws Exception {
+        doDeploy(admin, bundles, true);
+    }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/META-INF/spring/gshell-obr.xml b/karaf/gshell/gshell-obr/src/main/resources/META-INF/spring/gshell-obr.xml
new file mode 100644
index 0000000..250051f
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/META-INF/spring/gshell-obr.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:osgi="http://www.springframework.org/schema/osgi"
+       xmlns:osgix="http://www.springframework.org/schema/osgi-compendium"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+       xsi:schemaLocation="
+  http://www.springframework.org/schema/beans
+  http://www.springframework.org/schema/beans/spring-beans.xsd
+  http://www.springframework.org/schema/osgi
+  http://www.springframework.org/schema/osgi/spring-osgi.xsd
+  http://www.springframework.org/schema/osgi-compendium
+  http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd
+  http://www.springframework.org/schema/util
+  http://www.springframework.org/schema/util/spring-util.xsd
+  http://servicemix.apache.org/schema/servicemix-gshell
+  http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+    <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+    <gshell:command-bundle>
+        <gshell:command name="obr/addUrl">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.obr.AddUrlCommand" />
+        </gshell:command>
+        <gshell:command name="obr/deploy">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.obr.DeployCommand" />
+        </gshell:command>
+        <gshell:command name="obr/info">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.obr.InfoCommand" />
+        </gshell:command>
+        <gshell:command name="obr/list">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.obr.ListCommand" />
+        </gshell:command>
+        <gshell:command name="obr/listUrl">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.obr.ListUrlCommand" />
+        </gshell:command>
+        <gshell:command name="obr/removeUrl">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.obr.RemoveUrlCommand" />
+        </gshell:command>
+        <gshell:command name="obr/refreshUrl">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.obr.RefreshUrlCommand" />
+        </gshell:command>
+        <gshell:command name="obr/source">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.obr.SourceCommand" />
+        </gshell:command>
+        <gshell:command name="obr/start">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.obr.StartCommand" />
+        </gshell:command>
+    </gshell:command-bundle>
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/AddUrlCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/AddUrlCommand.properties
new file mode 100644
index 0000000..acf1131
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/AddUrlCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Add a list of repository URLs to the OBR service.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/DeployCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/DeployCommand.properties
new file mode 100644
index 0000000..d518148
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/DeployCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Deploy a list of bundles using OBR
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/InfoCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/InfoCommand.properties
new file mode 100644
index 0000000..23a2f59
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/InfoCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Print informations about OBR bundles
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/ListCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/ListCommand.properties
new file mode 100644
index 0000000..d73f3b7
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/ListCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=List OBR bundles
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/ListUrlCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/ListUrlCommand.properties
new file mode 100644
index 0000000..40b9fc6
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/ListUrlCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Display the repository URLs currently associated with the OBR service.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/RefreshUrlCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/RefreshUrlCommand.properties
new file mode 100644
index 0000000..0a4553f
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/RefreshUrlCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Reload the repositories to obtain a fresh list of bundles.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/RemoveUrlCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/RemoveUrlCommand.properties
new file mode 100644
index 0000000..259dfa1
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/RemoveUrlCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Remove a list of repository URLs from the OBR service.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/SourceCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/SourceCommand.properties
new file mode 100644
index 0000000..76b6888
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/SourceCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Download the sources for an OBR bundle
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/StartCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/StartCommand.properties
new file mode 100644
index 0000000..809ab4d
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/StartCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Deploy and start a list of bundles using OBR
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/pom.xml b/karaf/gshell/gshell-osgi/pom.xml
new file mode 100644
index 0000000..d232750
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/pom.xml
@@ -0,0 +1,87 @@
+<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">
+
+    <!--
+
+        Licensed to the Apache Software Foundation (ASF) under one or more
+        contributor license agreements.  See the NOTICE file distributed with
+        this work for additional information regarding copyright ownership.
+        The ASF licenses this file to You under the Apache License, Version 2.0
+        (the "License"); you may not use this file except in compliance with
+        the License.  You may obtain a copy of the License at
+
+           http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.servicemix.kernel.gshell</groupId>
+        <artifactId>gshell</artifactId>
+        <version>1.2.0-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.apache.servicemix.kernel.gshell</groupId>
+    <artifactId>org.apache.servicemix.kernel.gshell.osgi</artifactId>
+    <packaging>bundle</packaging>
+    <version>1.2.0-SNAPSHOT</version>
+    <name>Apache ServiceMix Kernel :: GShell OSGi Commands</name>
+
+    <description>
+        Provides the OSGi GShell commands
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.servicemix.kernel.gshell</groupId>
+            <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.osgi</groupId>
+            <artifactId>spring-osgi-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.osgi</groupId>
+            <artifactId>spring-osgi-extender</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+                        <Export-Package>
+                            org.apache.servicemix.kernel.gshell.osgi*;version=${project.version};-split-package:=merge-first
+                        </Export-Package>
+                        <Import-Package>
+                            org.apache.geronimo.gshell.wisdom.command,
+                            org.apache.geronimo.gshell.wisdom.registry,
+                            org.apache.servicemix.kernel.gshell.core,
+                            *
+                        </Import-Package>
+                        <Private-Package>!*</Private-Package>
+                        <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundleCommand.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundleCommand.java
new file mode 100644
index 0000000..5a3fd3f
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundleCommand.java
@@ -0,0 +1,79 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.startlevel.StartLevel;
+
+public abstract class BundleCommand extends OsgiCommandSupport {
+
+    @Argument(required = true, index = 0)
+    long id;
+
+    @Option(name = "--force")
+    boolean force;
+
+    protected Object doExecute() throws Exception {
+        Bundle bundle = getBundleContext().getBundle(id);
+        if (bundle == null) {
+            io.out.println("Bundle " + id + " not found");
+            return Result.FAILURE;
+        }
+        if (!force) {
+            ServiceReference ref = getBundleContext().getServiceReference(StartLevel.class.getName());
+            if (ref != null) {
+                StartLevel sl = getService(StartLevel.class, ref);
+                if (sl != null) {
+                    int level = sl.getBundleStartLevel(bundle);
+                    if (level < 50) {
+                        for (;;) {
+                            StringBuffer sb = new StringBuffer();
+                            io.err.print("You are about to access a system bundle.  Do you want to continue (yes/no): ");
+                            io.err.flush();
+                            for (;;) {
+                                int c = io.in.read();
+                                if (c < 0) {
+                                    return Result.FAILURE;
+                                }
+                                io.err.print((char) c);
+                                if (c == '\r' || c == '\n') {
+                                    break;
+                                }
+                                sb.append((char) c);
+                            }
+                            String str = sb.toString();
+                            if ("yes".equals(str)) {
+                                break;
+                            }
+                            if ("no".equals(str)) {
+                                return Result.FAILURE;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        doExecute(bundle);
+        return Result.SUCCESS;
+    }
+
+    protected abstract void doExecute(Bundle bundle) throws Exception;
+}
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundleLevel.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundleLevel.java
new file mode 100644
index 0000000..cf64476
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundleLevel.java
@@ -0,0 +1,74 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.startlevel.StartLevel;
+
+public class BundleLevel extends BundleCommand {
+
+    @Argument(required = false, index = 1)
+    Integer level;
+
+    protected void doExecute(Bundle bundle) throws Exception {
+        // Get package admin service.
+        ServiceReference ref = getBundleContext().getServiceReference(StartLevel.class.getName());
+        if (ref == null) {
+            io.out.println("StartLevel service is unavailable.");
+            return;
+        }
+        StartLevel sl = getService(StartLevel.class, ref);
+        if (sl == null) {
+            io.out.println("StartLevel service is unavailable.");
+            return;
+        }
+
+        if (level == null) {
+            io.out.println("Level " + sl.getBundleStartLevel(bundle));
+        }
+        else if ((level < 50) && sl.getBundleStartLevel(bundle) > 50){
+            for (;;) {
+                StringBuffer sb = new StringBuffer();
+                io.err.println("You are about to designate bundle as a system bundle.  Do you want to continue (yes/no): ");
+                io.err.flush();
+                for (;;) {
+                    int c = io.in.read();
+                    if (c < 0) {
+                        return;
+                    }
+                    io.err.println((char) c);
+                    if (c == '\r' || c == '\n') {
+                        break;
+                    }
+                    sb.append((char) c);
+                }
+                String str = sb.toString();
+                if ("yes".equals(str)) {
+                    sl.setBundleStartLevel(bundle, level);
+                    break;
+                } else if ("no".equals(str)) {
+                    break;
+                }
+            }
+        } else {
+            sl.setBundleStartLevel(bundle, level);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundlesCommand.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundlesCommand.java
new file mode 100644
index 0000000..7b937ee
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundlesCommand.java
@@ -0,0 +1,48 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+
+public abstract class BundlesCommand extends OsgiCommandSupport {
+
+    @Argument(required = false, multiValued = true, description = "Bundle IDs")
+    List<Long> ids;
+
+    protected Object doExecute() throws Exception {
+        List<Bundle> bundles = new ArrayList<Bundle>();
+        if (ids != null && !ids.isEmpty()) {
+            for (long id : ids) {
+                Bundle bundle = getBundleContext().getBundle(id);
+                if (bundle == null) {
+                    io.err.println("Bundle ID" + id + " is invalid");
+                } else {
+                    bundles.add(bundle);
+                }
+            }
+        }
+        doExecute(bundles);
+        return Result.SUCCESS;
+    }
+
+    protected abstract void doExecute(List<Bundle> bundles) throws Exception;
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Headers.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Headers.java
new file mode 100644
index 0000000..0acf414
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Headers.java
@@ -0,0 +1,67 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+
+public class Headers extends OsgiCommandSupport {
+
+    @Argument(required = false, multiValued = true, description = "Bundles ids")
+    List<Long> ids;
+
+    protected Object doExecute() throws Exception {
+        if (ids != null && !ids.isEmpty()) {
+            for (long id : ids) {
+                Bundle bundle = getBundleContext().getBundle(id);
+                if (bundle != null) {
+                    printHeaders(bundle);
+                }
+                else {
+                    io.err.println("Bundle ID " + id + " is invalid.");
+                }
+            }
+        }
+        else {
+            Bundle[] bundles = getBundleContext().getBundles();
+            for (int i = 0; i < bundles.length; i++) {
+                printHeaders(bundles[i]);
+            }
+        }
+        return Result.SUCCESS;
+    }
+
+    protected void printHeaders(Bundle bundle) throws Exception {
+        String title = Util.getBundleName(bundle);
+        io.out.println("\n" + title);
+        io.out.println(Util.getUnderlineString(title));
+        Dictionary dict = bundle.getHeaders();
+        Enumeration keys = dict.keys();
+        while (keys.hasMoreElements())
+        {
+            Object k = (String) keys.nextElement();
+            Object v = dict.get(k);
+            io.out.println(k + " = " + Util.getValueString(v));
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/InstallBundle.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/InstallBundle.java
new file mode 100644
index 0000000..ad8d526
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/InstallBundle.java
@@ -0,0 +1,80 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+
+public class InstallBundle extends OsgiCommandSupport {
+
+    @Argument(required = true, multiValued = true, description = "Bundle URLs")
+    List<String> urls;
+
+    @Option(name = "-s", aliases={"--start"}, description="Start the bundles after installation")
+    boolean start;
+
+    protected Object doExecute() throws Exception {
+        List<Bundle> bundles = new ArrayList<Bundle>();
+        StringBuffer sb = new StringBuffer();
+        for (String url : urls) {
+            Bundle bundle = install(url, io.out, io.err);
+            if (bundle != null) {
+                bundles.add(bundle);
+                if (sb.length() > 0) {
+                    sb.append(", ");
+                }
+                sb.append(bundle.getBundleId());
+            }
+        }
+        if (start) {
+            for (Bundle bundle : bundles) {
+                bundle.start();
+            }
+        }
+        if (sb.toString().indexOf(',') > 0) {
+            io.out.println("Bundle IDs: " + sb.toString());
+        } else if (sb.length() > 0) {
+            io.out.println("Bundle ID: " + sb.toString());
+        }
+        return Result.SUCCESS;
+    }
+
+    protected Bundle install(String location, PrintWriter out, PrintWriter err) {
+        try {
+            return getBundleContext().installBundle(location, null);
+        } catch (IllegalStateException ex) {
+            err.println(ex.toString());
+        } catch (BundleException ex) {
+            if (ex.getNestedException() != null) {
+                err.println(ex.getNestedException().toString());
+            } else {
+                err.println(ex.toString());
+            }
+        } catch (Exception ex) {
+            err.println(ex.toString());
+        }
+        return null;
+    }
+
+}
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ListBundles.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ListBundles.java
new file mode 100644
index 0000000..00604f3
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ListBundles.java
@@ -0,0 +1,203 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.service.startlevel.StartLevel;
+
+public class ListBundles extends OsgiCommandSupport {
+
+    @Option(name = "-l", description = "Show locations")
+    boolean showLoc;
+
+    @Option(name = "-s", description = "Show symbolic name")
+    boolean showSymbolic;
+
+    @Option(name = "-u", description = "Show update")
+    boolean showUpdate;
+
+    private SpringApplicationListener springApplicationListener;
+
+    public SpringApplicationListener getSpringApplicationListener() {
+        return springApplicationListener;
+    }
+
+    public void setSpringApplicationListener(SpringApplicationListener springApplicationListener) {
+        this.springApplicationListener = springApplicationListener;
+    }
+
+    protected Object doExecute() throws Exception {
+        ServiceReference ref = getBundleContext().getServiceReference(StartLevel.class.getName());
+        StartLevel sl = null;
+        if (ref != null) {
+            sl = (StartLevel) getBundleContext().getService(ref);
+        }
+        if (sl == null) {
+            io.out.println("StartLevel service is unavailable.");
+        }
+
+        ServiceReference pkgref = getBundleContext().getServiceReference(PackageAdmin.class.getName());
+        PackageAdmin admin = null;
+        if (pkgref != null) {
+            admin = (PackageAdmin) getBundleContext().getService(pkgref);
+            if (admin == null) {
+                io.out.println("PackageAdmin service is unavailable.");
+            }
+        }
+
+        Bundle[] bundles = getBundleContext().getBundles();
+        if (bundles != null) {
+            // Display active start level.
+            if (sl != null) {
+                io.out.println("START LEVEL " + sl.getStartLevel());
+            }
+
+            // Print column headers.
+            String msg = " Name";
+            if (showLoc) {
+               msg = " Location";
+            }
+            else if (showSymbolic) {
+               msg = " Symbolic name";
+            }
+            else if (showUpdate) {
+               msg = " Update location";
+            }
+            String level = (sl == null) ? "" : "  Level ";
+            io.out.println("   ID   State         Spring   " + level + msg);
+            for (int i = 0; i < bundles.length; i++) {
+                // Get the bundle name or location.
+                String name = (String) bundles[i].getHeaders().get(Constants.BUNDLE_NAME);
+                // If there is no name, then default to symbolic name.
+                name = (name == null) ? bundles[i].getSymbolicName() : name;
+                // If there is no symbolic name, resort to location.
+                name = (name == null) ? bundles[i].getLocation() : name;
+
+                // Overwrite the default value is the user specifically
+                // requested to display one or the other.
+                if (showLoc) {
+                    name = bundles[i].getLocation();
+                }
+                else if (showSymbolic) {
+                    name = bundles[i].getSymbolicName();
+                    name = (name == null) ? "<no symbolic name>" : name;
+                }
+                else if (showUpdate) {
+                    name = (String) bundles[i].getHeaders().get(Constants.BUNDLE_UPDATELOCATION);
+                    name = (name == null) ? bundles[i].getLocation() : name;
+                }
+                // Show bundle version if not showing location.
+                String version = (String) bundles[i].getHeaders().get(Constants.BUNDLE_VERSION);
+                name = (!showLoc && !showUpdate && (version != null)) ? name + " (" + version + ")" : name;
+                long l = bundles[i].getBundleId();
+                String id = String.valueOf(l);
+                if (sl == null) {
+                    level = "1";
+                }
+                else {
+                    level = String.valueOf(sl.getBundleStartLevel(bundles[i]));
+                }
+                while (level.length() < 5) {
+                    level = " " + level;
+                }
+                while (id.length() < 4) {
+                    id = " " + id;
+                }
+                io.out.println("[" + id + "] ["
+                    + getStateString(bundles[i])
+                    + "] [" + getSpringStateString(bundles[i])
+                    + "] [" + level + "] " + name);
+
+                if (admin != null) {
+                    Bundle[] fragments = admin.getFragments(bundles[i]);
+                    Bundle[] hosts = admin.getHosts(bundles[i]);
+
+                    if (fragments != null) {
+                        io.out.print("                                       Fragments: ");
+                        int ii = 0;
+                        for (Bundle fragment : fragments) {
+                            ii++;
+                            io.out.print(fragment.getBundleId());
+                            if ((fragments.length > 1) && ii < (fragments.length)) {
+                                io.out.print(",");
+                            }
+                        }
+                        io.out.println();
+                    }
+
+                    if (hosts != null) {
+                        io.out.print("                                       Hosts: ");
+                        int ii = 0;
+                        for (Bundle host : hosts) {
+                            ii++;
+                            io.out.print(host.getBundleId());
+                            if ((hosts.length > 1) && ii < (hosts.length)) {
+                                io.out.print(",");
+                            }
+                        }
+                        io.out.println();
+                    }
+
+                }
+            }
+        }
+        else {
+            io.out.println("There are no installed bundles.");
+        }
+
+        getBundleContext().ungetService(ref);
+        getBundleContext().ungetService(pkgref);
+
+        return Result.SUCCESS;
+    }
+
+    public String getStateString(Bundle bundle)
+    {
+        int state = bundle.getState();
+        if (state == Bundle.ACTIVE) {
+            return "Active     ";
+        } else if (state == Bundle.INSTALLED) {
+            return "Installed  ";
+        } else if (state == Bundle.RESOLVED) {
+            return "Resolved   ";
+        } else if (state == Bundle.STARTING) {
+            return "Starting   ";
+        } else if (state == Bundle.STOPPING) {
+            return "Stopping   ";
+        } else {
+            return "Unknown    ";
+        }
+    }
+
+    public String getSpringStateString(Bundle bundle) {
+        SpringApplicationListener.SpringState state = springApplicationListener.getSpringState(bundle);
+        if (state == SpringApplicationListener.SpringState.Waiting) {
+            return "Waiting";
+        } else if (state == SpringApplicationListener.SpringState.Started) {
+            return "Started";
+        } else if (state == SpringApplicationListener.SpringState.Failed) {
+            return "Failed ";
+        } else {
+            return "       ";
+        }
+    }
+}
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ListServices.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ListServices.java
new file mode 100644
index 0000000..3753e1a
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ListServices.java
@@ -0,0 +1,174 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.geronimo.gshell.command.Command;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+
+public class ListServices extends OsgiCommandSupport {
+
+    @Option(name = "-a", description = "Show all")
+    boolean showAll;
+
+    @Option(name = "-u", description = "Show in use")
+    boolean inUse;
+
+    @Argument(required = false, multiValued = true, description = "Bundles ids")
+    List<Long> ids;
+
+    protected Object doExecute() throws Exception {
+        if (ids != null && !ids.isEmpty()) {
+            for (long id : ids) {
+                Bundle bundle = getBundleContext().getBundle(id);
+                if (bundle != null) {
+                    boolean headerPrinted = false;
+                    boolean needSeparator = false;
+                    ServiceReference[] refs = null;
+
+                    // Get registered or in-use services.
+                    if (inUse) {
+                        refs = bundle.getServicesInUse();
+                    } else {
+                        refs = bundle.getRegisteredServices();
+                    }
+
+                    // Print properties for each service.
+                    for (int refIdx = 0;
+                         (refs != null) && (refIdx < refs.length);
+                         refIdx++) {
+                        String[] objectClass = (String[])
+                                refs[refIdx].getProperty("objectClass");
+
+                        // Determine if we need to print the service, depending
+                        // on whether it is a command service or not.
+                        boolean print = true;
+                        for (int ocIdx = 0;
+                             !showAll && (ocIdx < objectClass.length);
+                             ocIdx++) {
+                            if (objectClass[ocIdx].equals(Command.class.getName())) {
+                                print = false;
+                            }
+                        }
+
+                        // Print header if we have not already done so.
+                        if (!headerPrinted) {
+                            headerPrinted = true;
+                            String title = Util.getBundleName(bundle);
+                            title = (inUse)
+                                    ? title + " uses:"
+                                    : title + " provides:";
+                            io.out.println("");
+                            io.out.println(title);
+                            io.out.println(Util.getUnderlineString(title));
+                        }
+
+                        if (showAll || print) {
+                            // Print service separator if necessary.
+                            if (needSeparator) {
+                                io.out.println("----");
+                            }
+
+                            // Print service properties.
+                            String[] keys = refs[refIdx].getPropertyKeys();
+                            for (int keyIdx = 0;
+                                 (keys != null) && (keyIdx < keys.length);
+                                 keyIdx++) {
+                                Object v = refs[refIdx].getProperty(keys[keyIdx]);
+                                io.out.println(
+                                        keys[keyIdx] + " = " + Util.getValueString(v));
+                            }
+
+                            needSeparator = true;
+                        }
+                    }
+                } else {
+                    io.err.println("Bundle ID " + id + " is invalid.");
+                }
+            }
+        }
+        else
+        {
+            Bundle[] bundles = getBundleContext().getBundles();
+            if (bundles != null)
+            {
+                // TODO: Sort list.
+                for (int bundleIdx = 0; bundleIdx < bundles.length; bundleIdx++)
+                {
+                    boolean headerPrinted = false;
+                    ServiceReference[] refs = null;
+
+                    // Get registered or in-use services.
+                    if (inUse)
+                    {
+                        refs = bundles[bundleIdx].getServicesInUse();
+                    }
+                    else
+                    {
+                        refs = bundles[bundleIdx].getRegisteredServices();
+                    }
+
+                    for (int refIdx = 0; (refs != null) && (refIdx < refs.length); refIdx++)
+                    {
+                        String[] objectClass = (String[])
+                            refs[refIdx].getProperty("objectClass");
+
+                        // Determine if we need to print the service, depending
+                        // on whether it is a command service or not.
+                        boolean print = true;
+                        for (int ocIdx = 0;
+                            !showAll && (ocIdx < objectClass.length);
+                            ocIdx++)
+                        {
+                            if (objectClass[ocIdx].equals(Command.class.getName()))
+                            {
+                                print = false;
+                            }
+                        }
+
+                        // Print the service if necessary.
+                        if (showAll || print)
+                        {
+                            if (!headerPrinted)
+                            {
+                                headerPrinted = true;
+                                String title = Util.getBundleName(bundles[bundleIdx]);
+                                title = (inUse)
+                                    ? title + " uses:"
+                                    : title + " provides:";
+                                io.out.println("\n" + title);
+                                io.out.println(Util.getUnderlineString(title));
+                            }
+                            io.out.println(Util.getValueString(objectClass));
+                        }
+                    }
+                }
+            }
+            else
+            {
+                io.out.println("There are no registered services.");
+            }
+        }
+        return Result.SUCCESS;
+    }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/RefreshBundle.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/RefreshBundle.java
new file mode 100644
index 0000000..e554b3c
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/RefreshBundle.java
@@ -0,0 +1,60 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+public class RefreshBundle extends OsgiCommandSupport {
+
+    @Argument(required = false)
+    Long id;
+
+    protected Object doExecute() throws Exception {
+        // Get package admin service.
+        ServiceReference ref = getBundleContext().getServiceReference(PackageAdmin.class.getName());
+        if (ref == null) {
+            io.out.println("PackageAdmin service is unavailable.");
+            return Result.FAILURE;
+        }
+        try {
+            PackageAdmin pa = (PackageAdmin) getBundleContext().getService(ref);
+            if (pa == null) {
+                io.out.println("PackageAdmin service is unavailable.");
+                return Result.FAILURE;
+            }
+            if (id == null) {
+                pa.refreshPackages(null);
+            }
+            else {
+                Bundle bundle = getBundleContext().getBundle(id);
+                if (bundle == null) {
+                    io.out.println("Bundle " + id + " not found");
+                    return Result.FAILURE;
+                }
+                pa.refreshPackages(new Bundle[] { bundle });
+            }
+        }
+        finally {
+            getBundleContext().ungetService(ref);
+        }
+        return Result.SUCCESS;
+    }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ResolveBundle.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ResolveBundle.java
new file mode 100644
index 0000000..607ef91
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ResolveBundle.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.osgi;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+public class ResolveBundle extends BundleCommand {
+
+    protected void doExecute(Bundle bundle) throws Exception {
+        // Get package admin service.
+        ServiceReference ref = getBundleContext().getServiceReference(PackageAdmin.class.getName());
+        if (ref == null) {
+            io.out.println("PackageAdmin service is unavailable.");
+            return;
+        }
+        try {
+            PackageAdmin pa = (PackageAdmin) getBundleContext().getService(ref);
+            if (pa == null) {
+                io.out.println("PackageAdmin service is unavailable.");
+                return;
+            }
+            pa.resolveBundles(new Bundle[] { bundle });
+        }
+        finally {
+            getBundleContext().ungetService(ref);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/RestartBundle.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/RestartBundle.java
new file mode 100644
index 0000000..6793ecb
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/RestartBundle.java
@@ -0,0 +1,28 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.osgi.framework.Bundle;
+
+public class RestartBundle extends BundleCommand {
+
+    protected void doExecute(Bundle bundle) throws Exception {
+        bundle.stop();
+        bundle.start();
+    }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Shutdown.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Shutdown.java
new file mode 100644
index 0000000..76e8081
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Shutdown.java
@@ -0,0 +1,41 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+
+/**
+ * Command to shut down ServiceMix Kernel
+ */
+public class Shutdown extends OsgiCommandSupport {
+
+    protected Object doExecute() throws Exception {
+        new Thread() {
+            public void run() {
+                try {
+                    Bundle bundle = getBundleContext().getBundle(0);
+                    bundle.stop();
+                } catch (Exception e) {
+                    log.error("Error when shutting down ServiceMix Kernel", e);
+                }
+            }
+        }.start();
+        return Result.SUCCESS;
+    }
+
+}
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/SpringApplicationListener.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/SpringApplicationListener.java
new file mode 100644
index 0000000..240bd0a
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/SpringApplicationListener.java
@@ -0,0 +1,102 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleListener;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.osgi.context.BundleContextAware;
+import org.springframework.osgi.context.event.OsgiBundleApplicationContextEvent;
+import org.springframework.osgi.context.event.OsgiBundleApplicationContextListener;
+import org.springframework.osgi.context.event.OsgiBundleContextFailedEvent;
+import org.springframework.osgi.context.event.OsgiBundleContextRefreshedEvent;
+import org.springframework.osgi.extender.event.BootstrappingDependencyEvent;
+import org.springframework.osgi.service.importer.event.OsgiServiceDependencyEvent;
+import org.springframework.osgi.service.importer.event.OsgiServiceDependencyWaitStartingEvent;
+
+public class SpringApplicationListener implements OsgiBundleApplicationContextListener,
+                                                  BundleListener, BundleContextAware,
+                                                  InitializingBean, DisposableBean {
+
+    public static enum SpringState {
+        Unknown,
+        Waiting,
+        Started,
+        Failed,
+    }
+
+    private static final Log LOG = LogFactory.getLog(SpringApplicationListener.class);
+
+    private final Map<Long, SpringState> states;
+    private BundleContext bundleContext;
+
+    public SpringApplicationListener() {
+        this.states = new ConcurrentHashMap<Long, SpringState>();
+    }
+
+    public SpringState getSpringState(Bundle bundle) {
+        SpringState state = states.get(bundle.getBundleId());
+        if (state == null || bundle.getState() != Bundle.ACTIVE) {
+            state = SpringState.Unknown;
+        }
+        return state;
+    }
+
+    public void onOsgiApplicationEvent(OsgiBundleApplicationContextEvent event) {
+        SpringState state = null;
+        if (event instanceof BootstrappingDependencyEvent) {
+            OsgiServiceDependencyEvent de = ((BootstrappingDependencyEvent) event).getDependencyEvent();
+            if (de instanceof OsgiServiceDependencyWaitStartingEvent) {
+                state = SpringState.Waiting;
+            }
+        } else if (event instanceof OsgiBundleContextFailedEvent) {
+            state = SpringState.Failed;
+        } else if (event instanceof OsgiBundleContextRefreshedEvent) {
+            state = SpringState.Started;
+        }
+        if (state != null) {
+            LOG.debug("Spring app state changed to " + state + " for bundle " + event.getBundle().getBundleId());
+            states.put(event.getBundle().getBundleId(), state);
+        }
+    }
+
+    public void bundleChanged(BundleEvent event) {
+        if (event.getType() == BundleEvent.UNINSTALLED) {
+            states.remove(event.getBundle().getBundleId());
+        }
+    }
+
+    public void setBundleContext(BundleContext bundleContext) {
+        this.bundleContext = bundleContext;
+    }
+
+    public void afterPropertiesSet() throws Exception {
+        bundleContext.addBundleListener(this);
+    }
+
+    public void destroy() throws Exception {
+        bundleContext.removeBundleListener(this);
+    }
+}
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StartBundle.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StartBundle.java
new file mode 100644
index 0000000..6639802
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StartBundle.java
@@ -0,0 +1,27 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.osgi.framework.Bundle;
+
+public class StartBundle extends BundleCommand {
+
+    protected void doExecute(Bundle bundle) throws Exception {
+        bundle.start();
+    }
+
+}
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StartLevel.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StartLevel.java
new file mode 100644
index 0000000..a04a688
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StartLevel.java
@@ -0,0 +1,55 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.ServiceReference;
+
+public class StartLevel extends OsgiCommandSupport {
+
+    @Argument(required = false, index = 0)
+    Integer level;
+
+    protected Object doExecute() throws Exception {
+        // Get package admin service.
+        ServiceReference ref = getBundleContext().getServiceReference(org.osgi.service.startlevel.StartLevel.class.getName());
+        if (ref == null) {
+            io.out.println("StartLevel service is unavailable.");
+            return null;
+        }
+        try {
+            org.osgi.service.startlevel.StartLevel sl = (org.osgi.service.startlevel.StartLevel) getBundleContext().getService(ref);
+            if (sl == null) {
+                io.out.println("StartLevel service is unavailable.");
+                return null;
+            }
+
+            if (level == null) {
+                io.out.println("Level " + sl.getStartLevel());
+            }
+            else {
+                sl.setStartLevel(level);
+            }
+        }
+        finally {
+            getBundleContext().ungetService(ref);
+        }
+        return Result.SUCCESS;
+    }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StopBundle.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StopBundle.java
new file mode 100644
index 0000000..867a0a0
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StopBundle.java
@@ -0,0 +1,27 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.osgi.framework.Bundle;
+
+public class StopBundle extends BundleCommand {
+
+    protected void doExecute(Bundle bundle) throws Exception {
+        bundle.stop();
+    }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/UninstallBundle.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/UninstallBundle.java
new file mode 100644
index 0000000..f92cc9b
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/UninstallBundle.java
@@ -0,0 +1,27 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.osgi.framework.Bundle;
+
+public class UninstallBundle extends BundleCommand {
+
+    protected void doExecute(Bundle bundle) throws Exception {
+        bundle.uninstall();
+    }
+
+}
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/UpdateBundle.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/UpdateBundle.java
new file mode 100644
index 0000000..1e02e6f
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/UpdateBundle.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.kernel.gshell.osgi;
+
+import java.io.InputStream;
+import java.net.URL;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.framework.Bundle;
+
+public class UpdateBundle extends BundleCommand {
+
+	@Argument(required = false, description = "Bundle location", index=1)
+	String location;
+
+	protected void doExecute(Bundle bundle) throws Exception {
+		if (location != null) {
+			InputStream is = new URL(location).openStream();
+			bundle.update(is);
+		} else {
+			bundle.update();
+		}
+	}
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Util.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Util.java
new file mode 100644
index 0000000..c8481d6
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Util.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.servicemix.kernel.gshell.osgi;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+
+public class Util
+{
+    public static String getBundleName(Bundle bundle)
+    {
+        if (bundle != null)
+        {
+            String name = (String) bundle.getHeaders().get(Constants.BUNDLE_NAME);
+            return (name == null)
+                ? "Bundle " + Long.toString(bundle.getBundleId())
+                : name + " (" + Long.toString(bundle.getBundleId()) + ")";
+        }
+        return "[STALE BUNDLE]";
+    }
+
+    private static StringBuffer m_sb = new StringBuffer();
+
+    public static String getUnderlineString(String s)
+    {
+        synchronized (m_sb)
+        {
+            m_sb.delete(0, m_sb.length());
+            for (int i = 0; i < s.length(); i++)
+            {
+                m_sb.append('-');
+            }
+            return m_sb.toString();
+        }
+    }
+
+    public static String getValueString(Object obj)
+    {
+        synchronized (m_sb)
+        {
+            if (obj instanceof String)
+            {
+                return (String) obj;
+            }
+            else if (obj instanceof String[])
+            {
+                String[] array = (String[]) obj;
+                m_sb.delete(0, m_sb.length());
+                for (int i = 0; i < array.length; i++)
+                {
+                    if (i != 0)
+                    {
+                        m_sb.append(", ");
+                    }
+                    m_sb.append(array[i].toString());
+                }
+                return m_sb.toString();
+            }
+            else if (obj instanceof Boolean)
+            {
+                return ((Boolean) obj).toString();
+            }
+            else if (obj instanceof Long)
+            {
+                return ((Long) obj).toString();
+            }
+            else if (obj instanceof Integer)
+            {
+                return ((Integer) obj).toString();
+            }
+            else if (obj instanceof Short)
+            {
+                return ((Short) obj).toString();
+            }
+            else if (obj instanceof Double)
+            {
+                return ((Double) obj).toString();
+            }
+            else if (obj instanceof Float)
+            {
+                return ((Float) obj).toString();
+            }
+            else if (obj == null)
+            {
+                return "null";
+            }
+            else
+            {
+                return obj.toString();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/META-INF/spring/gshell-osgi.xml b/karaf/gshell/gshell-osgi/src/main/resources/META-INF/spring/gshell-osgi.xml
new file mode 100644
index 0000000..7f06ebf
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/META-INF/spring/gshell-osgi.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:osgi="http://www.springframework.org/schema/osgi"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+       xsi:schemaLocation="
+  http://www.springframework.org/schema/beans
+  http://www.springframework.org/schema/beans/spring-beans.xsd
+  http://www.springframework.org/schema/util
+  http://www.springframework.org/schema/util/spring-util.xsd
+  http://www.springframework.org/schema/osgi
+  http://www.springframework.org/schema/osgi/spring-osgi.xsd
+  http://servicemix.apache.org/schema/servicemix-gshell
+  http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+    <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+    <gshell:command-bundle>
+        <gshell:command name="osgi/bundle-level">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.BundleLevel" />
+        </gshell:command>
+        <gshell:command name="osgi/headers">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.Headers" />
+        </gshell:command>
+        <gshell:command name="osgi/install">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.InstallBundle" />
+        </gshell:command>
+        <gshell:command name="osgi/list">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.ListBundles">
+                <property name="springApplicationListener" ref="springApplicationListener" />
+            </gshell:action>
+        </gshell:command>
+        <gshell:command name="osgi/ls">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.ListServices" />
+        </gshell:command>
+        <gshell:command name="osgi/refresh">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.RefreshBundle" />
+        </gshell:command>
+        <gshell:command name="osgi/update">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.UpdateBundle" />
+        </gshell:command>
+        <gshell:command name="osgi/resolve">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.ResolveBundle" />
+        </gshell:command>
+        <gshell:command name="osgi/restart">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.RestartBundle" />
+        </gshell:command>
+        <gshell:command name="osgi/shutdown">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.Shutdown" />
+        </gshell:command>
+        <gshell:command name="osgi/start">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.StartBundle" />
+        </gshell:command>
+        <gshell:command name="osgi/start-level">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.StartLevel" />
+        </gshell:command>
+        <gshell:command name="osgi/stop">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.StopBundle" />
+        </gshell:command>
+        <gshell:command name="osgi/uninstall">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.UninstallBundle" />
+        </gshell:command>
+    </gshell:command-bundle>
+
+    <bean id="springApplicationListener" class="org.apache.servicemix.kernel.gshell.osgi.SpringApplicationListener" />
+
+    <osgi:service ref="springApplicationListener" interface="org.springframework.osgi.context.event.OsgiBundleApplicationContextListener" />
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/BundleLevel.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/BundleLevel.properties
new file mode 100644
index 0000000..9a9bee9
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/BundleLevel.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Get or set the start level of a given bundle
+
+command.manual=\
+  TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/Headers.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/Headers.properties
new file mode 100644
index 0000000..7d53e5f
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/Headers.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Display OSGi headers
+
+command.manual=\
+  TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/InstallBundle.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/InstallBundle.properties
new file mode 100644
index 0000000..85858db
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/InstallBundle.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Install bundle
+
+command.manual=\
+  TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ListBundles.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ListBundles.properties
new file mode 100644
index 0000000..204792a
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ListBundles.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=List bundles
+
+command.manual=\
+  TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ListServices.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ListServices.properties
new file mode 100644
index 0000000..1f8ccf9
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ListServices.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=List services
+
+command.manual=\
+  TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/RefreshBundle.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/RefreshBundle.properties
new file mode 100644
index 0000000..db60c15
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/RefreshBundle.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Refresh bundle
+
+command.manual=\
+  TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ResolveBundle.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ResolveBundle.properties
new file mode 100644
index 0000000..9f5de69
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ResolveBundle.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Resolve bundle
+
+command.manual=\
+  TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/RestartBundle.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/RestartBundle.properties
new file mode 100644
index 0000000..762aa40
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/RestartBundle.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Restart bundle
+
+command.manual=\
+  TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/Shutdown.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/Shutdown.properties
new file mode 100644
index 0000000..940f5f6
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/Shutdown.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Shutdown OSGi
+
+command.manual=\
+  TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StartBundle.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StartBundle.properties
new file mode 100644
index 0000000..7a6e7ae
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StartBundle.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Start bundle
+
+command.manual=\
+  TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StartLevel.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StartLevel.properties
new file mode 100644
index 0000000..1b9761f
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StartLevel.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Get or set the start level
+
+command.manual=\
+  TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StopBundle.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StopBundle.properties
new file mode 100644
index 0000000..40fc360
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StopBundle.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Stop bundle
+
+command.manual=\
+  TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/UninstallBundle.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/UninstallBundle.properties
new file mode 100644
index 0000000..36351ae
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/UninstallBundle.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Uninstall bundle
+
+command.manual=\
+  TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/UpdateBundle.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/UpdateBundle.properties
new file mode 100644
index 0000000..9b07581
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/UpdateBundle.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Update bundle
+
+command.manual=\
+  TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-packages/pom.xml b/karaf/gshell/gshell-packages/pom.xml
new file mode 100644
index 0000000..e127f98
--- /dev/null
+++ b/karaf/gshell/gshell-packages/pom.xml
@@ -0,0 +1,88 @@
+<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">
+
+    <!--
+
+        Licensed to the Apache Software Foundation (ASF) under one or more
+        contributor license agreements.  See the NOTICE file distributed with
+        this work for additional information regarding copyright ownership.
+        The ASF licenses this file to You under the Apache License, Version 2.0
+        (the "License"); you may not use this file except in compliance with
+        the License.  You may obtain a copy of the License at
+
+           http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.servicemix.kernel.gshell</groupId>
+        <artifactId>gshell</artifactId>
+        <version>1.2.0-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.apache.servicemix.kernel.gshell</groupId>
+    <artifactId>org.apache.servicemix.kernel.gshell.packages</artifactId>
+    <packaging>bundle</packaging>
+    <version>1.2.0-SNAPSHOT</version>
+    <name>Apache ServiceMix Kernel :: GShell PackageAdmin Commands</name>
+
+    <description>
+        Provides the PackageAdmin GShell commands
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.servicemix.kernel.gshell</groupId>
+            <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.osgi</groupId>
+            <artifactId>spring-osgi-core</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+                        <Export-Package>
+                            org.apache.servicemix.kernel.gshell.packages*;version=${project.version};-split-package:=merge-first
+                        </Export-Package>
+                        <Import-Package>
+                            org.apache.geronimo.gshell.wisdom.command,
+                            org.apache.geronimo.gshell.wisdom.registry,
+                            org.apache.servicemix.kernel.gshell.core,
+                            *
+                        </Import-Package>
+                        <Private-Package>!*</Private-Package>
+                        <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/ExportsCommand.java b/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/ExportsCommand.java
new file mode 100644
index 0000000..7bb15f1
--- /dev/null
+++ b/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/ExportsCommand.java
@@ -0,0 +1,83 @@
+/*
+ * 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.servicemix.kernel.gshell.packages;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+public class ExportsCommand extends PackageCommandSupport {
+
+    @Option(name = "-i", aliases = { "--imports"}, description = "List bundles importing the packages")
+    boolean imports;
+
+    @Argument(required = false, multiValued = true, description = "bundle ids")
+    List<Long> ids;
+
+    protected void doExecute(PackageAdmin admin) throws Exception {
+        if (ids != null && !ids.isEmpty()) {
+            for (long id : ids) {
+                Bundle bundle = getBundleContext().getBundle(id);
+                if (bundle != null) {
+                    printExports(io.out, bundle, admin.getExportedPackages(bundle));
+                } else {
+                    io.err.println("Bundle ID " + id + " is invalid.");
+                }
+            }
+        }
+        else {
+            printExports(io.out, null, admin.getExportedPackages((Bundle) null));
+        }
+    }
+
+    protected void printExports(PrintWriter out, Bundle target, ExportedPackage[] exports) {
+        if ((exports != null) && (exports.length > 0)) {
+            for (int i = 0; i < exports.length; i++) {
+                Bundle bundle = exports[i].getExportingBundle();
+                out.print(getBundleName(bundle));
+                out.println(": " + exports[i]);
+                if (imports) {
+                    Bundle[] bs = exports[i].getImportingBundles();
+                    if (bs != null) {
+                        for (Bundle b : bs) {
+                            out.println("\t" + getBundleName(b));
+                        }
+                    }
+                }
+            }
+        } else {
+            out.println(getBundleName(target) + ": No active exported packages.");
+        }
+    }
+
+    public static String getBundleName(Bundle bundle) {
+        if (bundle != null) {
+            String name = (String) bundle.getHeaders().get(Constants.BUNDLE_NAME);
+            return (name == null)
+                ? "Bundle " + Long.toString(bundle.getBundleId())
+                : name + " (" + Long.toString(bundle.getBundleId()) + ")";
+        }
+        return "[STALE BUNDLE]";
+    }
+
+}
diff --git a/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/ImportsCommand.java b/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/ImportsCommand.java
new file mode 100644
index 0000000..7d109cb
--- /dev/null
+++ b/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/ImportsCommand.java
@@ -0,0 +1,93 @@
+/*
+ * 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.servicemix.kernel.gshell.packages;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+public class ImportsCommand extends PackageCommandSupport {
+
+    @Argument(required = false, multiValued = true, description = "bundle ids")
+    List<Long> ids;
+
+    protected void doExecute(PackageAdmin admin) throws Exception {
+        Map<Long, List<ExportedPackage>> packages = new HashMap<Long, List<ExportedPackage>>();
+        ExportedPackage[] exported = admin.getExportedPackages((Bundle) null);
+        for (ExportedPackage pkg : exported) {
+            Bundle[] bundles = pkg.getImportingBundles();
+            if (bundles != null) {
+                for (Bundle b : bundles) {
+                    List<ExportedPackage> p = packages.get(b.getBundleId());
+                    if (p == null) {
+                        p = new ArrayList<ExportedPackage>();
+                        packages.put(b.getBundleId(), p);
+                    }
+                    p.add(pkg);
+                }
+            }
+        }
+        if (ids != null && !ids.isEmpty()) {
+            for (long id : ids) {
+                Bundle bundle = getBundleContext().getBundle(id);
+                if (bundle != null) {
+                    printImports(io.out, bundle, packages.get(bundle.getBundleId()));
+                } else {
+                    io.err.println("Bundle ID " + id + " is invalid.");
+                }
+            }
+        }
+        else {
+            List<ExportedPackage> pkgs = new ArrayList<ExportedPackage>();
+            for (List<ExportedPackage> l : packages.values()) {
+                pkgs.addAll(l);
+            }
+            printImports(io.out, null, pkgs);
+        }
+    }
+
+    protected void printImports(PrintWriter out, Bundle target, List<ExportedPackage> imports) {
+        if ((imports != null) && (imports.size() > 0)) {
+            for (ExportedPackage p : imports) {
+                Bundle bundle = p.getExportingBundle();
+                out.print(getBundleName(bundle));
+                out.println(": " + p);
+            }
+        } else {
+            out.println(getBundleName(target) + ": No active imported packages.");
+        }
+    }
+
+    public static String getBundleName(Bundle bundle) {
+        if (bundle != null) {
+            String name = (String) bundle.getHeaders().get(Constants.BUNDLE_NAME);
+            return (name == null)
+                ? "Bundle " + Long.toString(bundle.getBundleId())
+                : name + " (" + Long.toString(bundle.getBundleId()) + ")";
+        }
+        return "[STALE BUNDLE]";
+    }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/PackageCommandSupport.java b/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/PackageCommandSupport.java
new file mode 100644
index 0000000..ef99b94
--- /dev/null
+++ b/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/PackageCommandSupport.java
@@ -0,0 +1,55 @@
+/*
+ * 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.servicemix.kernel.gshell.packages;
+
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+/**
+ * Abstract class from which all commands related to the PackageAdmin
+ * service should derive.
+ * This command retrieves a reference to the PackageAdmin service before
+ * calling another method to actually process the command.
+ */
+public abstract class PackageCommandSupport extends OsgiCommandSupport {
+
+    protected Object doExecute() throws Exception {
+        // Get package admin service.
+        ServiceReference ref = getBundleContext().getServiceReference(PackageAdmin.class.getName());
+        if (ref == null) {
+            io.out.println("PackageAdmin service is unavailable.");
+            return null;
+        }
+        try {
+            PackageAdmin admin = (PackageAdmin) getBundleContext().getService(ref);
+            if (admin == null) {
+                io.out.println("PackageAdmin service is unavailable.");
+                return null;
+            }
+
+            doExecute(admin);
+        }
+        finally {
+            getBundleContext().ungetService(ref);
+        }
+        return null;
+    }
+
+    protected abstract void doExecute(PackageAdmin admin) throws Exception;
+
+}
diff --git a/karaf/gshell/gshell-packages/src/main/resources/META-INF/spring/gshell-packages.xml b/karaf/gshell/gshell-packages/src/main/resources/META-INF/spring/gshell-packages.xml
new file mode 100644
index 0000000..9e3060d
--- /dev/null
+++ b/karaf/gshell/gshell-packages/src/main/resources/META-INF/spring/gshell-packages.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:osgi="http://www.springframework.org/schema/osgi"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+       xsi:schemaLocation="
+  http://www.springframework.org/schema/beans
+  http://www.springframework.org/schema/beans/spring-beans.xsd
+  http://www.springframework.org/schema/util
+  http://www.springframework.org/schema/util/spring-util.xsd
+  http://www.springframework.org/schema/osgi
+  http://www.springframework.org/schema/osgi/spring-osgi.xsd
+  http://servicemix.apache.org/schema/servicemix-gshell
+  http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+    <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+    <gshell:command-bundle>
+        <gshell:command name="packages/exports">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.packages.ExportsCommand" />
+        </gshell:command>
+        <gshell:command name="packages/imports">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.packages.ImportsCommand" />
+        </gshell:command>
+    </gshell:command-bundle>
+
+</beans>
diff --git a/karaf/gshell/gshell-packages/src/main/resources/org/apache/servicemix/kernel/gshell/packages/ExportsCommand.properties b/karaf/gshell/gshell-packages/src/main/resources/org/apache/servicemix/kernel/gshell/packages/ExportsCommand.properties
new file mode 100644
index 0000000..9487118
--- /dev/null
+++ b/karaf/gshell/gshell-packages/src/main/resources/org/apache/servicemix/kernel/gshell/packages/ExportsCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Print exported packages.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-packages/src/main/resources/org/apache/servicemix/kernel/gshell/packages/ImportsCommand.properties b/karaf/gshell/gshell-packages/src/main/resources/org/apache/servicemix/kernel/gshell/packages/ImportsCommand.properties
new file mode 100644
index 0000000..917d860
--- /dev/null
+++ b/karaf/gshell/gshell-packages/src/main/resources/org/apache/servicemix/kernel/gshell/packages/ImportsCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Print imported packages.
+
+command.manual=\
+  TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-wrapper/pom.xml b/karaf/gshell/gshell-wrapper/pom.xml
new file mode 100644
index 0000000..341e837
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/pom.xml
@@ -0,0 +1,115 @@
+<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">
+
+    <!--
+
+        Licensed to the Apache Software Foundation (ASF) under one or more
+        contributor license agreements.  See the NOTICE file distributed with
+        this work for additional information regarding copyright ownership.
+        The ASF licenses this file to You under the Apache License, Version 2.0
+        (the "License"); you may not use this file except in compliance with
+        the License.  You may obtain a copy of the License at
+
+           http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.servicemix.kernel.gshell</groupId>
+        <artifactId>gshell</artifactId>
+        <version>1.2.0-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.apache.servicemix.kernel.gshell</groupId>
+    <artifactId>org.apache.servicemix.kernel.gshell.wrapper</artifactId>
+    <packaging>bundle</packaging>
+    <version>1.2.0-SNAPSHOT</version>
+    <name>Apache ServiceMix Kernel :: GShell Service Wrapper</name>
+
+    <description>
+        Provides the Service Wrapper GShell integration
+    </description>
+
+    <properties>
+        <gshell.osgi.import>
+            org.apache.servicemix.kernel.main.spi.*;resolution:=optional,
+            org.apache.geronimo.gshell*,
+        </gshell.osgi.import>
+        <gshell.osgi.export>
+        </gshell.osgi.export>
+        <gshell.osgi.private>
+            org.apache.servicemix.kernel.gshell.wrapper.*,
+        </gshell.osgi.private>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.servicemix.kernel.gshell</groupId>
+            <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.osgi</groupId>
+            <artifactId>spring-osgi-core</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>${pom.basedir}/src/main/resources</directory>
+                <includes>
+                    <include>**/*</include>
+                </includes>
+            </resource>
+            <resource>
+                <directory>${pom.basedir}/src/main/filtered-resources</directory>
+                <filtering>true</filtering>
+                <includes>
+                    <include>**/*</include>
+                </includes>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>Main</mainClass>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+                        <Export-Package>org.apache.servicemix.kernel.gshell.wrapper.*;version=${project.version}
+                        </Export-Package>
+                        <Import-Package>
+                            org.apache.geronimo.gshell.wisdom.command,
+                            org.apache.geronimo.gshell.wisdom.registry,
+                            org.apache.servicemix.kernel.gshell.core,
+                            *
+                        </Import-Package>
+                        <Private-Package>!*</Private-Package>
+                        <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-wrapper/src/main/java/org/apache/servicemix/kernel/gshell/wrapper/InstallCommand.java b/karaf/gshell/gshell-wrapper/src/main/java/org/apache/servicemix/kernel/gshell/wrapper/InstallCommand.java
new file mode 100644
index 0000000..f58c3cc
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/java/org/apache/servicemix/kernel/gshell/wrapper/InstallCommand.java
@@ -0,0 +1,360 @@
+/*
+ * 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.servicemix.kernel.gshell.wrapper;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Scanner;
+
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.geronimo.gshell.io.PumpStreamHandler;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+
+/**
+ * Installs this ServiceMix instance as a service in your operating systems. 
+ *
+ * @version $Rev: 603634 $ $Date: 2007-12-12 16:07:16 +0100 (Wed, 12 Dec 2007) $
+ */
+public class InstallCommand extends OsgiCommandSupport
+{
+	
+    @Option(name="-n", aliases={"--name"}, description="The service name that will be used when installing the service.")
+    private String name="servicemix";
+    @Option(name="-d", aliases={"--display"}, description="The display name of the service.")
+    private String displayName;
+    @Option(name="-D", aliases={"--description"}, description="The description of the service.")
+    private String description="";
+    @Option(name="-s", aliases={"--start-type"}, description="Mode in which the service is installed.  AUTO_START or DEMAND_START")
+    private String startType="AUTO_START";
+
+    protected Object doExecute() throws Exception {
+    	
+    	try {
+    		String name = getName();    		
+    		File base = new File(System.getProperty("servicemix.base"));
+    		File bin = new File(base, "bin");
+    		File etc = new File(base, "etc");
+    		File lib = new File(base, "lib");
+    		
+			HashMap<String, String> props = new HashMap<String, String>();
+			props.put("${servicemix.home}", System.getProperty("servicemix.home"));
+			props.put("${servicemix.base}", base.getPath());
+			props.put("${name}", name);
+			props.put("${displayName}", getDisplayName());
+			props.put("${description}", getDescription());
+			props.put("${startType}", getStartType());
+			
+			String os = System.getProperty("os.name", "Unknown");
+			File serviceFile=null;
+			if( os.startsWith("Win") ) {
+				mkdir(bin);
+				copyResourceTo(new File(bin, name+"-wrapper.exe"), "windows/servicemix-wrapper.exe", false);
+				serviceFile = new File(bin,name+"-service.bat");
+				copyFilteredResourceTo(serviceFile, "windows/servicemix-service.bat", props);
+				mkdir(lib);
+				copyResourceTo(new File(bin, "wrapper.dll"), "windows/wrapper.dll", false);								
+			} else if( os.startsWith("Mac OS X") ) {
+				mkdir(bin);
+				
+				File file = new File(bin, name+"-wrapper");
+				copyResourceTo(file, "macosx/servicemix-wrapper", false);
+				chmod(file, "a+x");
+				
+				serviceFile = new File(bin,name+"-service");
+				copyFilteredResourceTo(serviceFile, "unix/servicemix-service", props);
+				chmod(serviceFile, "a+x");
+				
+				mkdir(lib);
+				copyResourceTo(new File(lib, "libwrapper.jnilib"), "macosx/libwrapper.jnilib", false);
+				
+				// TODO: figure out how to hook in the service that it starts up
+				// when the machine boots up.
+			} else if( os.startsWith("Linux") ) {
+				mkdir(bin);
+				
+				File file = new File(bin, name+"-wrapper");
+				copyResourceTo(file, "linux/servicemix-wrapper", false);
+				chmod(file, "a+x");
+
+				serviceFile = new File(bin,name+"-service");
+				copyFilteredResourceTo(serviceFile, "unix/servicemix-service", props);
+				chmod(serviceFile, "a+x");
+				
+				mkdir(lib);
+				copyResourceTo(new File(lib, "libwrapper.so"), "linux/libwrapper.so", false);
+				
+				// TODO: figure out how to hook in the service that it starts up
+				// when the machine boots up.
+			} else {
+		        io.out.println("Your operating system '"+os+"' is not currently supported.");
+		        return 1;
+			}
+
+    		// Install the wrapper jar to the lib directory..
+			mkdir(lib);
+			copyResourceTo(new File(lib, "servicemix-wrapper.jar"), "all/servicemix-wrapper.jar", false);
+			mkdir(etc);
+			File wrapperConf = new File(etc,name+"-wrapper.conf");
+			copyFilteredResourceTo(wrapperConf, "all/servicemix-wrapper.conf", props);
+
+			io.out.println("");
+			io.out.println("Setup complete.  You may want to tweak the JVM properties in the wrapper configuration file: "+wrapperConf.getPath());
+			io.out.println("before installing and starting the service.");
+			io.out.println("");
+			if( os.startsWith("Win") ) {
+				io.out.println("");
+				io.out.println("To install the service, run: ");
+				io.out.println("  C:> "+serviceFile.getPath()+" install");
+				io.out.println("");
+				io.out.println("Once installed, to start the service run: ");
+				io.out.println("  C:> net start \""+name+"\"");
+				io.out.println("");
+				io.out.println("Once running, to stop the service run: ");
+				io.out.println("  C:> net stop \""+name+"\"");
+				io.out.println("");
+				io.out.println("Once stopped, to remove the installed the service run: ");
+				io.out.println("  C:> "+serviceFile.getPath()+" remove");
+				io.out.println("");
+			} else if( os.startsWith("Mac OS X") ) {
+				io.out.println("");
+				io.out.println("At this time it is not known how to get this service to start when the machine is rebooted.");
+				io.out.println("If you know how to install the following service script so that it gets started");
+				io.out.println("when OS X starts, please email dev@servicemix.apache.org and let us know how so");
+				io.out.println("we can update this message.");
+				io.out.println(" ");
+				io.out.println("  To start the service:");
+				io.out.println("    $ "+serviceFile.getPath()+" start");
+				io.out.println("");
+				io.out.println("  To stop the service:");
+				io.out.println("    $ "+serviceFile.getPath()+" stop");
+				io.out.println("");
+			} else if( os.startsWith("Linux") ) {
+				io.out.println("The way the service is installed depends upon your flavor of Linux.");
+				
+				// TODO: figure out if we can detect the Linux flavor
+				
+				io.out.println("");
+				io.out.println("@|cyan On Redhat/Fedora/CentOS Systems:|");
+				io.out.println("  To install the service:");
+				io.out.println("    $ ln -s "+serviceFile.getPath()+" /etc/init.d/");
+				io.out.println("    $ chkconfig "+serviceFile.getName()+" --add");
+				io.out.println("");
+				io.out.println("  To start the service when the machine is rebooted:");
+				io.out.println("    $ chkconfig "+serviceFile.getName()+" on");
+				io.out.println("");
+				io.out.println("  To disable starting the service when the machine is rebooted:");
+				io.out.println("    $ chkconfig "+serviceFile.getName()+" off");
+				io.out.println("");
+				io.out.println("  To start the service:");
+				io.out.println("    $ service "+serviceFile.getName()+" start");
+				io.out.println("");
+				io.out.println("  To stop the service:");
+				io.out.println("    $ service "+serviceFile.getName()+" stop");
+				io.out.println("");
+				io.out.println("  To uninstall the service :");
+				io.out.println("    $ chkconfig "+serviceFile.getName()+" --del");
+				io.out.println("    $ rm /etc/init.d/"+serviceFile.getName());
+				
+				io.out.println("");
+				io.out.println("@|cyan On Ubuntu/Debian Systems:|");
+				io.out.println("  To install the service:");
+				io.out.println("    $ ln -s "+serviceFile.getPath()+" /etc/init.d/");
+				io.out.println("");
+				io.out.println("  To start the service when the machine is rebooted:");
+				io.out.println("    $ update-rc.d "+serviceFile.getName()+" defaults");
+				io.out.println("");
+				io.out.println("  To disable starting the service when the machine is rebooted:");
+				io.out.println("    $ update-rc.d -f "+serviceFile.getName()+" remove");
+				io.out.println("");
+				io.out.println("  To start the service:");
+				io.out.println("    $ /etc/init.d/"+serviceFile.getName()+" start");
+				io.out.println("");
+				io.out.println("  To stop the service:");
+				io.out.println("    $ /etc/init.d/"+serviceFile.getName()+" stop");
+				io.out.println("");
+				io.out.println("  To uninstall the service :");
+				io.out.println("    $ rm /etc/init.d/"+serviceFile.getName());
+				
+			}
+
+			
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw e;
+		}
+
+        return 0;
+    }
+
+	private int chmod(File serviceFile, String mode) throws Exception {
+		ProcessBuilder builder = new ProcessBuilder();
+		builder.command("chmod", mode, serviceFile.getCanonicalPath());
+        Process p = builder.start();
+
+        PumpStreamHandler handler = new PumpStreamHandler(io.inputStream, io.outputStream, io.errorStream);
+        handler.attach(p);
+        handler.start();
+        int status = p.waitFor();
+        handler.stop();
+        return status;
+	}
+
+	private void copyResourceTo(File outFile, String resource, boolean text) throws Exception {
+		if( !outFile.exists() ) {
+	        io.out.println("Creating file: @|green "+outFile.getPath()+"|");
+			InputStream is = InstallCommand.class.getResourceAsStream(resource);
+			try {
+				if( text ) {
+					// Read it line at a time so that we can use the platform line ending when we write it out.
+					PrintStream out = new PrintStream(new FileOutputStream(outFile));
+					try { 
+						Scanner scanner = new Scanner(is);
+						while (scanner.hasNextLine() ) {
+							String line = scanner.nextLine();
+							io.out.println("writing: "+line);
+							out.println(line);
+						}
+					} finally {
+						safeClose(out);
+					}
+				} else {
+					// Binary so just write it out the way it came in.
+					FileOutputStream out = new FileOutputStream(outFile);
+					try {
+						int c=0;
+						while((c=is.read())>=0) {
+							out.write(c);
+						}
+					} finally {
+						safeClose(out);
+					}
+				}
+			} finally {
+				safeClose(is);
+			}
+		} else {
+	        io.out.println("@|red File allready exists|. Move it out of the way if you want it re-created: "+outFile.getPath()+"");
+		}
+	}
+	
+	private void copyFilteredResourceTo(File outFile, String resource, HashMap<String, String> props) throws Exception {
+		if( !outFile.exists() ) {
+	        io.out.println("Creating file: @|green "+outFile.getPath()+"|");
+			InputStream is = InstallCommand.class.getResourceAsStream(resource);
+			try {
+				// Read it line at a time so that we can use the platform line ending when we write it out.
+				PrintStream out = new PrintStream(new FileOutputStream(outFile));
+				try { 
+					Scanner scanner = new Scanner(is);
+					while (scanner.hasNextLine() ) {
+						String line = scanner.nextLine();
+						line = filter(line, props);
+						out.println(line);
+					}
+				} finally {
+					safeClose(out);
+				}
+			} finally {
+				safeClose(is);
+			}
+		} else {
+	        io.out.println("@|red File allready exists|. Move it out of the way if you want it re-created: "+outFile.getPath()+"");
+		}
+	}
+
+	private void safeClose(InputStream is) throws IOException {
+		if( is==null)
+			return;
+		try {
+			is.close();
+		} catch (Throwable ignore) {
+		}
+	}
+	
+	private void safeClose(OutputStream is) throws IOException {
+		if( is==null)
+			return;
+		try {
+			is.close();
+		} catch (Throwable ignore) {
+		}
+	}
+
+	private String filter(String line, HashMap<String, String> props) {
+		for (Map.Entry<String, String> i : props.entrySet()) {
+			int p1 = line.indexOf(i.getKey());
+			if( p1 >= 0 ) {
+				String l1 = line.substring(0, p1);
+				String l2 = line.substring(p1+i.getKey().length());
+				line = l1+i.getValue()+l2;
+			}
+		}
+		return line;
+	}
+
+	private void mkdir(File file) {
+		if( !file.exists() ) {
+	        io.out.println("Creating missing directory: @|green "+file.getPath()+"|");
+			file.mkdirs();
+		}
+	}
+
+	public String getName() {
+		if( name ==  null ) {
+    		File base = new File(System.getProperty("servicemix.base"));
+    		name = base.getName();
+		}
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getDisplayName() {
+		if( displayName == null ) {
+			displayName = getName();
+		}
+		return displayName;
+	}
+
+	public void setDisplayName(String displayName) {
+		this.displayName = displayName;
+	}
+
+	public String getDescription() {
+		return description;
+	}
+
+	public void setDescription(String description) {
+		this.description = description;
+	}
+
+	public String getStartType() {
+		return startType;
+	}
+
+	public void setStartType(String startType) {
+		this.startType = startType;
+	}
+}
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/META-INF/spring/gshell-wrapper.xml b/karaf/gshell/gshell-wrapper/src/main/resources/META-INF/spring/gshell-wrapper.xml
new file mode 100644
index 0000000..3eb5a60
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/META-INF/spring/gshell-wrapper.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:osgi="http://www.springframework.org/schema/osgi"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+       xsi:schemaLocation="
+  http://www.springframework.org/schema/beans
+  http://www.springframework.org/schema/beans/spring-beans.xsd
+  http://www.springframework.org/schema/util
+  http://www.springframework.org/schema/util/spring-util.xsd
+  http://www.springframework.org/schema/osgi
+  http://www.springframework.org/schema/osgi/spring-osgi.xsd
+  http://servicemix.apache.org/schema/servicemix-gshell
+  http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+    <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+    <gshell:command-bundle>
+        <gshell:command name="wrapper/install">
+            <gshell:action class="org.apache.servicemix.kernel.gshell.wrapper.InstallCommand" />
+        </gshell:command>
+    </gshell:command-bundle>
+
+</beans>
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/InstallCommand.properties b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/InstallCommand.properties
new file mode 100644
index 0000000..e5a5cb8
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/InstallCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Install ServiceMix Kernel as a system service in the OS.
+
+command.manual=\
+  TODO: install manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/all/servicemix-wrapper.conf b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/all/servicemix-wrapper.conf
new file mode 100644
index 0000000..021872d
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/all/servicemix-wrapper.conf
@@ -0,0 +1,124 @@
+# ------------------------------------------------------------------------
+# 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.
+# ------------------------------------------------------------------------
+
+#********************************************************************
+# Wrapper Properties
+#********************************************************************
+set.default.SERVICEMIX_HOME=${servicemix.home}
+set.default.SERVICEMIX_BASE=${servicemix.base}
+
+# Java Application
+wrapper.working.dir=%SERVICEMIX_BASE%
+wrapper.java.command=java
+wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp
+wrapper.java.classpath.1=%SERVICEMIX_BASE%/lib/servicemix-wrapper.jar
+wrapper.java.classpath.2=%SERVICEMIX_HOME%/lib/servicemix.jar
+wrapper.java.classpath.3=%SERVICEMIX_HOME%/lib/servicemix-jaas-boot.jar
+wrapper.java.library.path.1=%SERVICEMIX_BASE%/lib/
+
+# Application Parameters.  Add parameters as needed starting from 1
+wrapper.app.parameter.1=org.apache.servicemix.kernel.main.Main
+
+# JVM Parameters
+# note that n is the parameter number starting from 1.
+wrapper.java.additional.1=-Dservicemix.home=%SERVICEMIX_HOME%
+wrapper.java.additional.2=-Dservicemix.base=%SERVICEMIX_BASE%
+wrapper.java.additional.3=-Dcom.sun.management.jmxremote
+wrapper.java.additional.4=-Dservicemix.startLocalConsole=false
+wrapper.java.additional.5=-Dservicemix.startRemoteShell=true
+
+# Uncomment to enable jmx
+#wrapper.java.additional.n=-Dcom.sun.management.jmxremote.port=1616
+#wrapper.java.additional.n=-Dcom.sun.management.jmxremote.authenticate=false
+#wrapper.java.additional.n=-Dcom.sun.management.jmxremote.ssl=false
+
+# Uncomment to enable YourKit profiling
+#wrapper.java.additional.n=-Xrunyjpagent
+
+# Uncomment to enable remote debugging
+#wrapper.java.additional.n=-Xdebug -Xnoagent -Djava.compiler=NONE
+#wrapper.java.additional.n=-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005
+
+# Initial Java Heap Size (in MB)
+#wrapper.java.initmemory=3
+
+# Maximum Java Heap Size (in MB)
+wrapper.java.maxmemory=512
+
+
+#********************************************************************
+# Wrapper Logging Properties
+#********************************************************************
+# Format of output for the console.  (See docs for formats)
+wrapper.console.format=PM
+
+# Log Level for console output.  (See docs for log levels)
+wrapper.console.loglevel=INFO
+
+# Log file to use for wrapper output logging.
+wrapper.logfile=%SERVICEMIX_BASE%/data/log/wrapper.log
+
+# Format of output for the log file.  (See docs for formats)
+wrapper.logfile.format=LPTM
+
+# Log Level for log file output.  (See docs for log levels)
+wrapper.logfile.loglevel=INFO
+
+# Maximum size that the log file will be allowed to grow to before
+#  the log is rolled. Size is specified in bytes.  The default value
+#  of 0, disables log rolling.  May abbreviate with the 'k' (kb) or
+#  'm' (mb) suffix.  For example: 10m = 10 megabytes.
+wrapper.logfile.maxsize=10m
+
+# Maximum number of rolled log files which will be allowed before old
+#  files are deleted.  The default value of 0 implies no limit.
+wrapper.logfile.maxfiles=5
+
+# Log Level for sys/event log output.  (See docs for log levels)
+wrapper.syslog.loglevel=NONE
+
+#********************************************************************
+# Wrapper Windows Properties
+#********************************************************************
+# Title to use when running as a console
+wrapper.console.title=${name}
+
+#********************************************************************
+# Wrapper Windows NT/2000/XP Service Properties
+#********************************************************************
+# WARNING - Do not modify any of these properties when an application
+#  using this configuration file has been installed as a service.
+#  Please uninstall the service before modifying this section.  The
+#  service can then be reinstalled.
+
+# Name of the service
+wrapper.ntservice.name=${name}
+
+# Display name of the service
+wrapper.ntservice.displayname=${displayName}
+
+# Description of the service
+wrapper.ntservice.description=${description}
+
+# Service dependencies.  Add dependencies as needed starting from 1
+wrapper.ntservice.dependency.1=
+
+# Mode in which the service is installed.  AUTO_START or DEMAND_START
+wrapper.ntservice.starttype=${startType}
+
+# Allow the service to interact with the desktop.
+wrapper.ntservice.interactive=false
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/all/servicemix-wrapper.jar b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/all/servicemix-wrapper.jar
new file mode 100755
index 0000000..c766405
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/all/servicemix-wrapper.jar
Binary files differ
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/linux/libwrapper.so b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/linux/libwrapper.so
new file mode 100644
index 0000000..df25ec6
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/linux/libwrapper.so
Binary files differ
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/linux/servicemix-wrapper b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/linux/servicemix-wrapper
new file mode 100644
index 0000000..c58d4f7
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/linux/servicemix-wrapper
Binary files differ
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/macosx/libwrapper.jnilib b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/macosx/libwrapper.jnilib
new file mode 100644
index 0000000..ae5a14e
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/macosx/libwrapper.jnilib
Binary files differ
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/macosx/servicemix-wrapper b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/macosx/servicemix-wrapper
new file mode 100644
index 0000000..8c281b9
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/macosx/servicemix-wrapper
Binary files differ
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/unix/servicemix-service b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/unix/servicemix-service
new file mode 100755
index 0000000..d80c13b
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/unix/servicemix-service
@@ -0,0 +1,543 @@
+#! /bin/sh
+
+# ------------------------------------------------------------------------
+# 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.
+# ------------------------------------------------------------------------
+
+# Application
+APP_NAME="${name}"
+APP_LONG_NAME="${displayName}"
+
+# Wrapper
+WRAPPER_CMD="${servicemix.base}/bin/${APP_NAME}-wrapper"
+WRAPPER_CONF="${servicemix.base}/etc/${APP_NAME}-wrapper.conf"
+
+# Priority at which to run the wrapper.  See "man nice" for valid priorities.
+#  nice is only used if a priority is specified.
+PRIORITY=
+
+# Location of the pid file.
+PIDDIR="${servicemix.base}/data"
+
+# If uncommented, causes the Wrapper to be shutdown using an anchor file.
+#  When launched with the 'start' command, it will also ignore all INT and
+#  TERM signals.
+#IGNORE_SIGNALS=true
+
+# If specified, the Wrapper will be run as the specified user.
+# IMPORTANT - Make sure that the user has the required privileges to write
+#  the PID file and wrapper.log files.  Failure to be able to write the log
+#  file will cause the Wrapper to exit without any way to write out an error
+#  message.
+# NOTE - This will set the user which is used to run the Wrapper as well as
+#  the JVM and is not useful in situations where a privileged resource or
+#  port needs to be allocated prior to the user being changed.
+#RUN_AS_USER=
+
+# The following two lines are used by the chkconfig command. Change as is
+#  appropriate for your application.  They should remain commented.
+# chkconfig: 2345 20 80
+# description: ${displayName}
+
+# Do not modify anything beyond this point
+#-----------------------------------------------------------------------------
+
+# Get the fully qualified path to the script
+case $0 in
+    /*)
+        SCRIPT="$0"
+        ;;
+    *)
+        PWD=`pwd`
+        SCRIPT="$PWD/$0"
+        ;;
+esac
+
+# Resolve the true real path without any sym links.
+CHANGED=true
+while [ "X$CHANGED" != "X" ]
+do
+    # Change spaces to ":" so the tokens can be parsed.
+    SCRIPT=`echo $SCRIPT | sed -e 's; ;:;g'`
+    # Get the real path to this script, resolving any symbolic links
+    TOKENS=`echo $SCRIPT | sed -e 's;/; ;g'`
+    REALPATH=
+    for C in $TOKENS; do
+        REALPATH="$REALPATH/$C"
+        while [ -h "$REALPATH" ] ; do
+            LS="`ls -ld "$REALPATH"`"
+            LINK="`expr "$LS" : '.*-> \(.*\)$'`"
+            if expr "$LINK" : '/.*' > /dev/null; then
+                REALPATH="$LINK"
+            else
+                REALPATH="`dirname "$REALPATH"`""/$LINK"
+            fi
+        done
+    done
+    # Change ":" chars back to spaces.
+    REALPATH=`echo $REALPATH | sed -e 's;:; ;g'`
+
+    if [ "$REALPATH" = "$SCRIPT" ]
+    then
+        CHANGED=""
+    else
+        SCRIPT="$REALPATH"
+    fi
+done
+
+# Change the current directory to the location of the script
+cd "`dirname "$REALPATH"`"
+REALDIR=`pwd`
+
+# If the PIDDIR is relative, set its value relative to the full REALPATH to avoid problems if
+#  the working directory is later changed.
+FIRST_CHAR=`echo $PIDDIR | cut -c1,1`
+if [ "$FIRST_CHAR" != "/" ]
+then
+    PIDDIR=$REALDIR/$PIDDIR
+fi
+# Same test for WRAPPER_CMD
+FIRST_CHAR=`echo $WRAPPER_CMD | cut -c1,1`
+if [ "$FIRST_CHAR" != "/" ]
+then
+    WRAPPER_CMD=$REALDIR/$WRAPPER_CMD
+fi
+# Same test for WRAPPER_CONF
+FIRST_CHAR=`echo $WRAPPER_CONF | cut -c1,1`
+if [ "$FIRST_CHAR" != "/" ]
+then
+    WRAPPER_CONF=$REALDIR/$WRAPPER_CONF
+fi
+
+# Process ID
+ANCHORFILE="$PIDDIR/$APP_NAME.anchor"
+PIDFILE="$PIDDIR/$APP_NAME.pid"
+LOCKDIR="/var/lock/subsys"
+LOCKFILE="$LOCKDIR/$APP_NAME"
+pid=""
+
+# Resolve the location of the 'ps' command
+PSEXE="/usr/bin/ps"
+if [ ! -x $PSEXE ]
+then
+    PSEXE="/bin/ps"
+    if [ ! -x $PSEXE ]
+    then
+        echo "Unable to locate 'ps'."
+        echo "Please report this message along with the location of the command on your system."
+        exit 1
+    fi
+fi
+
+# Resolve the os
+DIST_OS=`uname -s | tr [:upper:] [:lower:] | tr -d [:blank:]`
+case "$DIST_OS" in
+    'sunos')
+        DIST_OS="solaris"
+        ;;
+    'hp-ux' | 'hp-ux64')
+        DIST_OS="hpux"
+        ;;
+    'darwin')
+        DIST_OS="macosx"
+        ;;
+    'unix_sv')
+        DIST_OS="unixware"
+        ;;
+esac
+
+# Resolve the architecture
+DIST_ARCH=`uname -p | tr [:upper:] [:lower:] | tr -d [:blank:]`
+if [ "$DIST_ARCH" = "unknown" ]
+then
+    DIST_ARCH=`uname -m | tr [:upper:] [:lower:] | tr -d [:blank:]`
+fi
+case "$DIST_ARCH" in
+    'amd64' | 'ia32' | 'ia64' | 'i386' | 'i486' | 'i586' | 'i686' | 'x86_64')
+        DIST_ARCH="x86"
+        ;;
+    'ip27')
+        DIST_ARCH="mips"
+        ;;
+    'power' | 'powerpc' | 'power_pc' | 'ppc64')
+        DIST_ARCH="ppc"
+        ;;
+    'pa_risc' | 'pa-risc')
+        DIST_ARCH="parisc"
+        ;;
+    'sun4u' | 'sparcv9')
+        DIST_ARCH="sparc"
+        ;;
+    '9000/800')
+        DIST_ARCH="parisc"
+        ;;
+esac
+
+# Decide on the wrapper binary to use.
+# If a 32-bit wrapper binary exists then it will work on 32 or 64 bit
+#  platforms, if the 64-bit binary exists then the distribution most
+#  likely wants to use long names.  Otherwise, look for the default.
+# For macosx, we also want to look for universal binaries.
+WRAPPER_TEST_CMD="$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-32"
+if [ -x $WRAPPER_TEST_CMD ]
+then
+    WRAPPER_CMD="$WRAPPER_TEST_CMD"
+else
+    if [ "$DIST_OS" = "macosx" ]
+    then
+        WRAPPER_TEST_CMD="$WRAPPER_CMD-$DIST_OS-universal-32"
+        if [ -x $WRAPPER_TEST_CMD ]
+        then
+            WRAPPER_CMD="$WRAPPER_TEST_CMD"
+        else
+            WRAPPER_TEST_CMD="$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-64"
+            if [ -x $WRAPPER_TEST_CMD ]
+            then
+                WRAPPER_CMD="$WRAPPER_TEST_CMD"
+            else
+                WRAPPER_TEST_CMD="$WRAPPER_CMD-$DIST_OS-universal-64"
+                if [ -x $WRAPPER_TEST_CMD ]
+                then
+                    WRAPPER_CMD="$WRAPPER_TEST_CMD"
+                else
+                    if [ ! -x $WRAPPER_CMD ]
+                    then
+                        echo "Unable to locate any of the following binaries:"
+                        echo "  $WRAPPER_CMD-$DIST_OS-$DIST_ARCH-32"
+                        echo "  $WRAPPER_CMD-$DIST_OS-universal-32"
+                        echo "  $WRAPPER_CMD-$DIST_OS-$DIST_ARCH-64"
+                        echo "  $WRAPPER_CMD-$DIST_OS-universal-64"
+                        echo "  $WRAPPER_CMD"
+                        exit 1
+                    fi
+                fi
+            fi
+        fi
+    else
+        WRAPPER_TEST_CMD="$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-64"
+        if [ -x $WRAPPER_TEST_CMD ]
+        then
+            WRAPPER_CMD="$WRAPPER_TEST_CMD"
+        else
+            if [ ! -x $WRAPPER_CMD ]
+            then
+                echo "Unable to locate any of the following binaries:"
+                echo "  $WRAPPER_CMD-$DIST_OS-$DIST_ARCH-32"
+                echo "  $WRAPPER_CMD-$DIST_OS-$DIST_ARCH-64"
+                echo "  $WRAPPER_CMD"
+                exit 1
+            fi
+        fi
+    fi
+fi
+
+# Build the nice clause
+if [ "X$PRIORITY" = "X" ]
+then
+    CMDNICE=""
+else
+    CMDNICE="nice -$PRIORITY"
+fi
+
+# Build the anchor file clause.
+if [ "X$IGNORE_SIGNALS" = "X" ]
+then
+   ANCHORPROP=
+   IGNOREPROP=
+else
+   ANCHORPROP=wrapper.anchorfile=$ANCHORFILE
+   IGNOREPROP=wrapper.ignore_signals=TRUE
+fi
+
+# Build the lock file clause.  Only create a lock file if the lock directory exists on this platform.
+if [ -d $LOCKDIR ]
+then
+    LOCKPROP=wrapper.lockfile=$LOCKFILE
+else
+    LOCKPROP=
+fi
+
+checkUser() {
+    # Check the configured user.  If necessary rerun this script as the desired user.
+    if [ "X$RUN_AS_USER" != "X" ]
+    then
+        # Resolve the location of the 'id' command
+        IDEXE="/usr/xpg4/bin/id"
+        if [ ! -x $IDEXE ]
+        then
+            IDEXE="/usr/bin/id"
+            if [ ! -x $IDEXE ]
+            then
+                echo "Unable to locate 'id'."
+                echo "Please report this message along with the location of the command on your system."
+                exit 1
+            fi
+        fi
+
+        if [ "`$IDEXE -u -n`" = "$RUN_AS_USER" ]
+        then
+            # Already running as the configured user.  Avoid password prompts by not calling su.
+            RUN_AS_USER=""
+        fi
+    fi
+    if [ "X$RUN_AS_USER" != "X" ]
+    then
+        # If LOCKPROP and $RUN_AS_USER are defined then the new user will most likely not be
+        # able to create the lock file.  The Wrapper will be able to update this file once it
+        # is created but will not be able to delete it on shutdown.  If $2 is defined then
+        # the lock file should be created for the current command
+        if [ "X$LOCKPROP" != "X" ]
+        then
+            if [ "X$2" != "X" ]
+            then
+                # Resolve the primary group
+                RUN_AS_GROUP=`groups $RUN_AS_USER | awk '{print $3}' | tail -1`
+                if [ "X$RUN_AS_GROUP" = "X" ]
+                then
+                    RUN_AS_GROUP=RUN_AS_USER
+                fi
+                touch $LOCKFILE
+                chown $RUN_AS_USER:$RUN_AS_GROUP $LOCKFILE
+            fi
+        fi
+
+        # Still want to change users, recurse.  This means that the user will only be
+        #  prompted for a password once.
+        su -m $RUN_AS_USER -c "$REALPATH $1"
+
+        # Now that we are the original user again, we may need to clean up the lock file.
+        if [ "X$LOCKPROP" != "X" ]
+        then
+            getpid
+            if [ "X$pid" = "X" ]
+            then
+                # Wrapper is not running so make sure the lock file is deleted.
+                if [ -f $LOCKFILE ]
+                then
+                    rm $LOCKFILE
+                fi
+            fi
+        fi
+
+        exit 0
+    fi
+}
+
+getpid() {
+    if [ -f $PIDFILE ]
+    then
+        if [ -r $PIDFILE ]
+        then
+            pid=`cat $PIDFILE`
+            if [ "X$pid" != "X" ]
+            then
+                # It is possible that 'a' process with the pid exists but that it is not the
+                #  correct process.  This can happen in a number of cases, but the most
+                #  common is during system startup after an unclean shutdown.
+                # The ps statement below looks for the specific wrapper command running as
+                #  the pid.  If it is not found then the pid file is considered to be stale.
+                pidtest=`$PSEXE -p $pid -o command | grep $WRAPPER_CMD | tail -1`
+                if [ "X$pidtest" = "X" ]
+                then
+                    # This is a stale pid file.
+                    rm -f $PIDFILE
+                    echo "Removed stale pid file: $PIDFILE"
+                    pid=""
+                fi
+            fi
+        else
+            echo "Cannot read $PIDFILE."
+            exit 1
+        fi
+    fi
+}
+
+testpid() {
+    pid=`$PSEXE -p $pid | grep $pid | grep -v grep | awk '{print $1}' | tail -1`
+    if [ "X$pid" = "X" ]
+    then
+        # Process is gone so remove the pid file.
+        rm -f $PIDFILE
+        pid=""
+    fi
+}
+
+console() {
+    echo "Running $APP_LONG_NAME..."
+    getpid
+    if [ "X$pid" = "X" ]
+    then
+        COMMAND_LINE="$CMDNICE $WRAPPER_CMD $WRAPPER_CONF wrapper.syslog.ident=$APP_NAME wrapper.pidfile=$PIDFILE $ANCHORPROP $LOCKPROP"
+        exec $COMMAND_LINE
+    else
+        echo "$APP_LONG_NAME is already running."
+        exit 1
+    fi
+}
+
+start() {
+    echo "Starting $APP_LONG_NAME..."
+    getpid
+    if [ "X$pid" = "X" ]
+    then
+        if [ ! -d ../../data ]; then
+            mkdir ../../data
+        fi
+        if [ ! -d ../../data/log ]; then
+            mkdir ../../data/log
+        fi
+        COMMAND_LINE="$CMDNICE $WRAPPER_CMD $WRAPPER_CONF wrapper.syslog.ident=$APP_NAME wrapper.pidfile=$PIDFILE wrapper.daemonize=TRUE $ANCHORPROP $IGNOREPROP $LOCKPROP"
+        exec $COMMAND_LINE
+    else
+        echo "$APP_LONG_NAME is already running."
+        exit 1
+    fi
+}
+
+stopit() {
+    echo "Stopping $APP_LONG_NAME..."
+    getpid
+    if [ "X$pid" = "X" ]
+    then
+        echo "$APP_LONG_NAME was not running."
+    else
+        if [ "X$IGNORE_SIGNALS" = "X" ]
+        then
+            # Running so try to stop it.
+            kill $pid
+            if [ $? -ne 0 ]
+            then
+                # An explanation for the failure should have been given
+                echo "Unable to stop $APP_LONG_NAME."
+                exit 1
+            fi
+        else
+            rm -f $ANCHORFILE
+            if [ -f $ANCHORFILE ]
+            then
+                # An explanation for the failure should have been given
+                echo "Unable to stop $APP_LONG_NAME."
+                exit 1
+            fi
+        fi
+
+        # We can not predict how long it will take for the wrapper to
+        #  actually stop as it depends on settings in wrapper.conf.
+        #  Loop until it does.
+        savepid=$pid
+        CNT=0
+        TOTCNT=0
+        while [ "X$pid" != "X" ]
+        do
+            # Show a waiting message every 5 seconds.
+            if [ "$CNT" -lt "5" ]
+            then
+                CNT=`expr $CNT + 1`
+            else
+                echo "Waiting for $APP_LONG_NAME to exit..."
+                CNT=0
+            fi
+            TOTCNT=`expr $TOTCNT + 1`
+
+            sleep 1
+
+            testpid
+        done
+
+        pid=$savepid
+        testpid
+        if [ "X$pid" != "X" ]
+        then
+            echo "Failed to stop $APP_LONG_NAME."
+            exit 1
+        else
+            echo "Stopped $APP_LONG_NAME."
+        fi
+    fi
+}
+
+status() {
+    getpid
+    if [ "X$pid" = "X" ]
+    then
+        echo "$APP_LONG_NAME is not running."
+        exit 1
+    else
+        echo "$APP_LONG_NAME is running ($pid)."
+        exit 0
+    fi
+}
+
+dump() {
+    echo "Dumping $APP_LONG_NAME..."
+    getpid
+    if [ "X$pid" = "X" ]
+    then
+        echo "$APP_LONG_NAME was not running."
+
+    else
+        kill -3 $pid
+
+        if [ $? -ne 0 ]
+        then
+            echo "Failed to dump $APP_LONG_NAME."
+            exit 1
+        else
+            echo "Dumped $APP_LONG_NAME."
+        fi
+    fi
+}
+
+case "$1" in
+
+    'console')
+        checkUser $1 touchlock
+        console
+        ;;
+
+    'start')
+        checkUser $1 touchlock
+        start
+        ;;
+
+    'stop')
+        checkUser $1
+        stopit
+        ;;
+
+    'restart')
+        checkUser $1 touchlock
+        stopit
+        start
+        ;;
+
+    'status')
+        checkUser $1
+        status
+        ;;
+
+    'dump')
+        checkUser $1
+        dump
+        ;;
+
+    *)
+        echo "Usage: $0 { console | start | stop | restart | status | dump }"
+        exit 1
+        ;;
+esac
+
+exit 0
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/servicemix-service.bat b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/servicemix-service.bat
new file mode 100644
index 0000000..df0160e
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/servicemix-service.bat
@@ -0,0 +1,50 @@
+@echo off
+
+REM ------------------------------------------------------------------------
+REM Licensed to the Apache Software Foundation (ASF) under one or more
+REM contributor license agreements.  See the NOTICE file distributed with
+REM this work for additional information regarding copyright ownership.
+REM The ASF licenses this file to You under the Apache License, Version 2.0
+REM (the "License"); you may not use this file except in compliance with
+REM the License.  You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM Unless required by applicable law or agreed to in writing, software
+REM distributed under the License is distributed on an "AS IS" BASIS,
+REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+REM See the License for the specific language governing permissions and
+REM limitations under the License.
+REM ------------------------------------------------------------------------
+
+setlocal
+
+set APP_NAME=${name}
+set APP_LONG_NAME=${displayName}
+set APP_BASE=${servicemix.base}
+
+if ""%1"" == ""run"" goto doRun
+if ""%1"" == ""install"" goto doInstall
+if ""%1"" == ""remove"" goto doRemove
+
+echo Usage:  servicemix-service ( commands ... )
+echo commands:
+echo   run               Start %APP_NAME% in the current console
+echo   install           Install %APP_NAME% as a Windows service
+echo   remove            Remove the %APP_NAME% Windows service
+goto end
+
+:doRun
+"%APP_BASE%\bin\%APP_NAME%-wrapper.exe" -c "%APP_BASE%\etc\%APP_NAME%-wrapper.conf"
+goto end
+
+:doInstall
+"%APP_BASE%\bin\%APP_NAME%-wrapper.exe" -i "%APP_BASE%\etc\%APP_NAME%-wrapper.conf"
+goto end
+
+:doRemove
+"%APP_BASE%\bin\%APP_NAME%-wrapper.exe" -r "%APP_BASE%\etc\%APP_NAME%-wrapper.conf"
+goto end
+
+:end
+if not "%PAUSE%" == "" pause
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/servicemix-wrapper.exe b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/servicemix-wrapper.exe
new file mode 100644
index 0000000..a46a2ac
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/servicemix-wrapper.exe
Binary files differ
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/wrapper.dll b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/wrapper.dll
new file mode 100644
index 0000000..37c4f33
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/wrapper.dll
Binary files differ
diff --git a/karaf/gshell/itests/pom.xml b/karaf/gshell/itests/pom.xml
new file mode 100644
index 0000000..c684ceb
--- /dev/null
+++ b/karaf/gshell/itests/pom.xml
@@ -0,0 +1,153 @@
+<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">
+
+    <!--
+
+        Licensed to the Apache Software Foundation (ASF) under one or more
+        contributor license agreements.  See the NOTICE file distributed with
+        this work for additional information regarding copyright ownership.
+        The ASF licenses this file to You under the Apache License, Version 2.0
+        (the "License"); you may not use this file except in compliance with
+        the License.  You may obtain a copy of the License at
+
+           http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.servicemix.kernel.gshell</groupId>
+        <artifactId>gshell</artifactId>
+        <version>1.2.0-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.apache.servicemix.kernel.gshell</groupId>
+    <artifactId>org.apache.servicemix.kernel.gshell.itests</artifactId>
+    <packaging>jar</packaging>
+    <version>1.2.0-SNAPSHOT</version>
+    <name>Apache ServiceMix Kernel :: GShell ITests</name>
+
+    <description>
+        Provides the OSGi GShell commands
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.servicemix.kernel.gshell</groupId>
+            <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.kernel.gshell</groupId>
+            <artifactId>org.apache.servicemix.kernel.gshell.log</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.kernel.gshell</groupId>
+            <artifactId>org.apache.servicemix.kernel.gshell.osgi</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.kernel.jaas</groupId>
+            <artifactId>org.apache.servicemix.kernel.jaas.boot</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.kernel.testing</groupId>
+            <artifactId>org.apache.servicemix.kernel.testing.support</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+        <artifactId>maven-resources-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>copy-resources</id>
+            <phase>validate</phase>
+            <goals>
+              <goal>copy-resources</goal>
+            </goals>
+            <configuration>
+              <outputDirectory>${pom.basedir}/target/test-classes/</outputDirectory>
+              <resources>          
+                <resource>
+                  <directory>${pom.basedir}/src/test/filtered-resources</directory>
+                  <filtering>true</filtering>
+                </resource>
+              </resources>              
+            </configuration>            
+          </execution>
+        </executions>
+      </plugin>
+            <!-- generate dependencies versions -->
+            <plugin>
+                <groupId>org.apache.servicemix.tooling</groupId>
+                <artifactId>depends-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>generate-depends-file</id>
+                        <goals>
+                            <goal>generate-depends-file</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>ci-build-profile</id>
+            <activation>
+                <property>
+                   <name>maven.repo.local</name>
+                </property>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                           <!-- when the local repo location has been specified, we need to pass on this information to PAX mvn url -->
+                           <argLine>-Dorg.ops4j.pax.url.mvn.localRepository=${maven.repo.local}</argLine>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <activation>
+                <os>
+                    <family>Windows</family>
+                </os>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <skip>true</skip>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/karaf/gshell/itests/src/test/filtered-resources/features.xml b/karaf/gshell/itests/src/test/filtered-resources/features.xml
new file mode 100644
index 0000000..8316b3f
--- /dev/null
+++ b/karaf/gshell/itests/src/test/filtered-resources/features.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+      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.
+-->
+<features>
+    <feature name="wrapper" version="${version}">
+        <bundle>mvn:org.apache.servicemix.kernel.gshell/org.apache.servicemix.kernel.gshell.wrapper/${version}</bundle>
+    </feature>
+    <feature name="obr" version="${version}">
+        <bundle>mvn:org.apache.felix/org.apache.felix.bundlerepository/${felix.bundlerepository.version}</bundle>
+        <bundle>mvn:org.apache.servicemix.kernel.gshell/org.apache.servicemix.kernel.gshell.obr/${version}</bundle>
+    </feature>
+</features>
diff --git a/karaf/gshell/itests/src/test/java/org/apache/servicemix/kernel/gshell/itests/CoreTest.java b/karaf/gshell/itests/src/test/java/org/apache/servicemix/kernel/gshell/itests/CoreTest.java
new file mode 100644
index 0000000..6f90dc2
--- /dev/null
+++ b/karaf/gshell/itests/src/test/java/org/apache/servicemix/kernel/gshell/itests/CoreTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.servicemix.kernel.gshell.itests;
+
+import org.apache.geronimo.gshell.commandline.CommandLineExecutionFailed;
+import org.apache.geronimo.gshell.registry.NoSuchCommandException;
+import org.apache.geronimo.gshell.shell.Shell;
+import org.apache.servicemix.kernel.testing.support.AbstractIntegrationTest;
+import org.osgi.framework.Bundle;
+
+public class CoreTest extends AbstractIntegrationTest {
+
+    static {
+        System.setProperty("servicemix.startLocalConsole", "false");
+        System.setProperty("servicemix.startRemoteShell", "false");
+    }
+
+    @Override
+    protected String getManifestLocation() {
+        return "classpath:org/apache/servicemix/kernel/gshell/itests/MANIFEST.MF";
+    }
+
+    @Override
+    protected String[] getTestBundlesNames() {
+        return new String[] {
+            getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.jline"),
+            getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.commons-httpclient"),
+            getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.commons-jexl"),
+            getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.commons-vfs"),
+            getBundle("org.apache.mina", "mina-core"),
+            getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.oro"),
+            getBundle("org.apache.servicemix.kernel.jaas", "org.apache.servicemix.kernel.jaas.config"),
+            getBundle("org.apache.sshd", "sshd-core"),
+            getBundle("org.apache.servicemix.kernel.gshell", "org.apache.servicemix.kernel.gshell.core"),
+            getBundle("org.apache.servicemix.kernel.gshell", "org.apache.servicemix.kernel.gshell.osgi")
+        };
+    }
+
+    public void testHelp() throws Exception {
+        Shell shell = getOsgiService(Shell.class);
+        shell.execute("help");
+    }
+
+    public void testInstallCommand() throws Exception {
+        Shell shell = getOsgiService(Shell.class);
+
+        try {
+            shell.execute("log/display");
+            fail("command should not exist");
+        } catch (CommandLineExecutionFailed e) {
+            assertNotNull(e.getCause());
+            assertTrue(e.getCause() instanceof NoSuchCommandException);
+        }
+
+        Bundle b = installBundle("org.apache.servicemix.kernel.gshell", "org.apache.servicemix.kernel.gshell.log", null, "jar");
+
+        shell.execute("log/display");
+
+        b.uninstall();
+
+        try {
+            shell.execute("log/display");
+            fail("command should not exist");
+        } catch (CommandLineExecutionFailed e) {
+            assertNotNull(e.getCause());
+            assertTrue(e.getCause() instanceof NoSuchCommandException);
+        }
+    }
+
+    public void testCommandGroup() throws Exception {
+        Shell shell = getOsgiService(Shell.class);
+        shell.execute("osgi");
+        shell.execute("help");
+        shell.execute("..");
+    }
+    
+    public void testInstallFeature() throws Exception {
+        Shell shell = getOsgiService(Shell.class);
+
+        try {
+            shell.execute("obr");
+            fail("command should not exist");
+        } catch (CommandLineExecutionFailed e) {
+            assertNotNull(e.getCause());
+            assertTrue(e.getCause() instanceof NoSuchCommandException);
+        }
+        try {
+            shell.execute("wrapper");
+            fail("command should not exist");
+        } catch (CommandLineExecutionFailed e) {
+            assertNotNull(e.getCause());
+            assertTrue(e.getCause() instanceof NoSuchCommandException);
+        }
+        String url = getClass().getClassLoader().getResource("features.xml").toString();
+        addFeatureRepo(url);
+        installFeature("obr");
+        installFeature("wrapper");
+        shell.execute("obr");
+        shell.execute("wrapper");
+    }
+
+    /**
+     * TODO: This test seems to fail, there must be a timing issue somewhere
+     *
+    public void testCommandGroupAfterInstall() throws Exception {
+        Bundle b = installBundle("org.apache.servicemix.kernel.gshell", "org.apache.servicemix.kernel.gshell.log", null, "jar");
+        Shell shell = getOsgiService(Shell.class);
+        shell.execute("log");
+        shell.execute("help");
+        shell.execute("..");
+    }
+     */
+
+
+}
diff --git a/karaf/gshell/itests/src/test/resources/log4j.properties b/karaf/gshell/itests/src/test/resources/log4j.properties
new file mode 100644
index 0000000..8cc85f8
--- /dev/null
+++ b/karaf/gshell/itests/src/test/resources/log4j.properties
@@ -0,0 +1,33 @@
+################################################################################
+#
+#    Licensed to the Apache Software Foundation (ASF) under one or more
+#    contributor license agreements.  See the NOTICE file distributed with
+#    this work for additional information regarding copyright ownership.
+#    The ASF licenses this file to You under the Apache License, Version 2.0
+#    (the "License"); you may not use this file except in compliance with
+#    the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+#
+################################################################################
+
+# Root logger
+log4j.rootLogger=INFO, stdout
+
+# CONSOLE appender not used by default
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+
+# File appender
+log4j.appender.out=org.apache.log4j.FileAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+log4j.appender.out.file=${servicemix.base}/data/log/servicemix.log
+log4j.appender.out.append=true
diff --git a/karaf/gshell/itests/src/test/resources/org/apache/servicemix/kernel/gshell/itests/MANIFEST.MF b/karaf/gshell/itests/src/test/resources/org/apache/servicemix/kernel/gshell/itests/MANIFEST.MF
new file mode 100644
index 0000000..df8a1a7
--- /dev/null
+++ b/karaf/gshell/itests/src/test/resources/org/apache/servicemix/kernel/gshell/itests/MANIFEST.MF
@@ -0,0 +1,30 @@
+Manifest-Version: 1.0
+License-00: .
+License-01: Licensed to the Apache Software Foundation (ASF) under one or more
+License-02: contributor license agreements.  See the NOTICE file distributed with
+License-03: this work for additional information regarding copyright ownership.
+License-04: The ASF licenses this file to You under the Apache License, Version 2.0
+License-05: (the "License"); you may not use this file except in compliance with
+License-06: the License.  You may obtain a copy of the License at
+License-07: .
+License-08:      http://www.apache.org/licenses/LICENSE-2.0
+License-09: .
+License-10: Unless required by applicable law or agreed to in writing, software
+License-11: distributed under the License is distributed on an "AS IS" BASIS,
+License-12: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+License-13: See the License for the specific language governing permissions and
+License-14: limitations under the License.
+License-15: .
+Bundle-Name: org.apache.servicemix.testing.itests
+Bundle-SymbolicName: org.apache.servicemix.testing.itests
+Bundle-Vendor: Spring Framework
+Bundle-Activator: org.springframework.osgi.test.JUnitTestActivator
+Import-Package: junit.framework,
+ org.osgi.framework;specification-version="1.3.0",
+ org.apache.commons.logging,
+ org.springframework.core.io,
+ org.springframework.osgi.test,
+ org.apache.servicemix.kernel.testing.support,
+ org.apache.geronimo.gshell.commandline,
+ org.apache.geronimo.gshell.registry,
+ org.apache.geronimo.gshell.shell
diff --git a/karaf/gshell/pom.xml b/karaf/gshell/pom.xml
new file mode 100644
index 0000000..3c2d24f
--- /dev/null
+++ b/karaf/gshell/pom.xml
@@ -0,0 +1,49 @@
+<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">
+
+    <!--
+
+        Licensed to the Apache Software Foundation (ASF) under one or more
+        contributor license agreements.  See the NOTICE file distributed with
+        this work for additional information regarding copyright ownership.
+        The ASF licenses this file to You under the Apache License, Version 2.0
+        (the "License"); you may not use this file except in compliance with
+        the License.  You may obtain a copy of the License at
+
+           http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.servicemix.kernel</groupId>
+        <artifactId>kernel</artifactId>
+        <version>1.2.0-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.apache.servicemix.kernel.gshell</groupId>
+    <artifactId>gshell</artifactId>
+    <packaging>pom</packaging>
+    <version>1.2.0-SNAPSHOT</version>
+    <name>Apache ServiceMix Kernel :: GShell</name>
+
+    <modules>
+        <module>gshell-core</module>
+        <module>gshell-osgi</module>
+        <module>gshell-admin</module>
+        <module>gshell-features</module>
+        <module>gshell-obr</module>
+        <module>gshell-wrapper</module>
+        <module>gshell-log</module>
+        <module>gshell-config</module>
+        <module>gshell-packages</module>
+        <module>itests</module>
+    </modules>
+
+</project>
\ No newline at end of file
diff --git a/karaf/gshell/src/test/configs/services/org.apache.servicemix.shell.properties b/karaf/gshell/src/test/configs/services/org.apache.servicemix.shell.properties
new file mode 100644
index 0000000..7779a61
--- /dev/null
+++ b/karaf/gshell/src/test/configs/services/org.apache.servicemix.shell.properties
@@ -0,0 +1,20 @@
+# 
+# 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.
+#
+#
+startLocalConsole=false
diff --git a/karaf/gshell/src/test/configs/services/org.ops4j.pax.logging.properties b/karaf/gshell/src/test/configs/services/org.ops4j.pax.logging.properties
new file mode 100644
index 0000000..23075bf
--- /dev/null
+++ b/karaf/gshell/src/test/configs/services/org.ops4j.pax.logging.properties
@@ -0,0 +1,34 @@
+# 
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed  under the  License is distributed on an "AS IS" BASIS,
+# WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
+# implied.
+#  
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+log4j.rootLogger=DEBUG, out
+
+log4j.logger.org.springframework=DEBUG
+
+# CONSOLE appender not used by default
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+
+# File appender
+log4j.appender.out=org.apache.log4j.FileAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+log4j.appender.out.file=target/gshell.log
+log4j.appender.out.append=true