FELIX-1547: OS shell level admin commands for Karaf

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@812616 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/karaf/assembly/src/main/descriptors/unix-bin.xml b/karaf/assembly/src/main/descriptors/unix-bin.xml
index 3ca944c..090f403 100644
--- a/karaf/assembly/src/main/descriptors/unix-bin.xml
+++ b/karaf/assembly/src/main/descriptors/unix-bin.xml
@@ -45,6 +45,15 @@
             <lineEnding>unix</lineEnding>
         </fileSet>        
         <fileSet>
+            <directory>target/classes/bin</directory>
+            <outputDirectory>/bin/</outputDirectory>
+            <lineEnding>unix</lineEnding>
+            <fileMode>0755</fileMode>
+            <includes>
+                <include>admin</include>
+            </includes>
+        </fileSet>
+        <fileSet>
             <directory>target/classes/etc</directory>
             <outputDirectory>/etc/</outputDirectory>
             <lineEnding>unix</lineEnding>
diff --git a/karaf/assembly/src/main/descriptors/windows-bin.xml b/karaf/assembly/src/main/descriptors/windows-bin.xml
index 0ab16dd..0a09975 100644
--- a/karaf/assembly/src/main/descriptors/windows-bin.xml
+++ b/karaf/assembly/src/main/descriptors/windows-bin.xml
@@ -46,6 +46,14 @@
           <lineEnding>dos</lineEnding>
       </fileSet>        
       <fileSet>
+          <directory>target/classes/bin</directory>
+          <outputDirectory>/bin/</outputDirectory>
+          <lineEnding>dos</lineEnding>
+          <includes>
+              <include>admin.bat</include>
+          </includes>
+      </fileSet>
+      <fileSet>
           <directory>target/classes/etc</directory>
           <outputDirectory>/etc/</outputDirectory>
           <lineEnding>dos</lineEnding>
diff --git a/karaf/assembly/src/main/distribution/unix-shell/bin/karaf b/karaf/assembly/src/main/distribution/unix-shell/bin/karaf
index 1a1d90b..d966b03 100755
--- a/karaf/assembly/src/main/distribution/unix-shell/bin/karaf
+++ b/karaf/assembly/src/main/distribution/unix-shell/bin/karaf
@@ -285,12 +285,12 @@
         KARAF_BASE=`cygpath --path --windows "$KARAF_BASE"`
         CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
     fi
-    exec $JAVA $JAVA_OPTS -Dkaraf.home="$KARAF_HOME" -Dkaraf.base="$KARAF_BASE" -Djava.util.logging.config.file=$KARAF_BASE/etc/java.util.logging.properties $OPTS -classpath "$CLASSPATH" org.apache.felix.karaf.main.Bootstrap $*
+    exec $JAVA $JAVA_OPTS -Dkaraf.home="$KARAF_HOME" -Dkaraf.base="$KARAF_BASE" -Djava.util.logging.config.file=$KARAF_BASE/etc/java.util.logging.properties $OPTS -classpath "$CLASSPATH" org.apache.felix.karaf.main.Bootstrap "$@"
 }
 
 main() {
     init
-    run $@
+    run "$@"
 }
 
-main $@
+main "$@"
diff --git a/karaf/assembly/src/main/filtered-resources/bin/admin b/karaf/assembly/src/main/filtered-resources/bin/admin
new file mode 100644
index 0000000..7b46968
--- /dev/null
+++ b/karaf/assembly/src/main/filtered-resources/bin/admin
@@ -0,0 +1,284 @@
+#!/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.
+#
+#
+
+DIRNAME=`dirname $0`
+PROGNAME=`basename $0`
+
+#
+# Check/Set up some easily accessible MIN/MAX params for JVM mem usage
+#
+
+if [ "x$JAVA_MIN_MEM" = "x" ]; then
+    JAVA_MIN_MEM=128M
+    export JAVA_MIN_MEM
+fi
+
+if [ "x$JAVA_MAX_MEM" = "x" ]; then
+    JAVA_MAX_MEM=512M
+    export JAVA_MAX_MEM
+fi
+
+warn() {
+    echo "${PROGNAME}: $*"
+}
+
+die() {
+    warn "$*"
+    exit 1
+}
+
+maybeSource() {
+    file="$1"
+    if [ -f "$file" ] ; then
+        . $file
+    fi
+}
+
+detectOS() {
+    # OS specific support (must be 'true' or 'false').
+    cygwin=false;
+    darwin=false;
+    aix=false;
+    os400=false;
+    case "`uname`" in
+        CYGWIN*)
+            cygwin=true
+            ;;
+        Darwin*)
+            darwin=true
+            ;;
+        AIX*)
+            aix=true
+            ;;
+        OS400*)
+            os400=true
+            ;;
+    esac
+    # For AIX, set an environment variable
+    if $aix; then
+         export LDR_CNTRL=MAXDATA=0xB0000000@DSA
+         export IBM_JAVA_HEAPDUMP_TEXT=true
+         echo $LDR_CNTRL
+    fi
+}
+
+unlimitFD() {
+    # Use the maximum available, or set MAX_FD != -1 to use that
+    if [ "x$MAX_FD" = "x" ]; then
+        MAX_FD="maximum"
+    fi
+
+    # Increase the maximum file descriptors if we can
+    if [ "$os400" = "false" ] && [ "$cygwin" = "false" ]; then
+        MAX_FD_LIMIT=`ulimit -H -n`
+        if [ "$MAX_FD_LIMIT" != 'unlimited' ]; then 
+            if [ $? -eq 0 ]; then
+                if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ]; then
+                    # use the system max
+                    MAX_FD="$MAX_FD_LIMIT"
+                fi
+
+                ulimit -n $MAX_FD > /dev/null
+                # echo "ulimit -n" `ulimit -n`
+                if [ $? -ne 0 ]; then
+                    warn "Could not set maximum file descriptor limit: $MAX_FD"
+                fi
+            else
+                warn "Could not query system maximum file descriptor limit: $MAX_FD_LIMIT"
+            fi
+        fi
+    fi
+}
+
+locateHome() {
+    if [ "x$KARAF_HOME" != "x" ]; then
+        warn "Ignoring predefined value for KARAF_HOME"
+    fi
+    
+    # In POSIX shells, CDPATH may cause cd to write to stdout
+    (unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+    KARAF_HOME=`cd $DIRNAME/..; pwd`
+    if [ ! -d "$KARAF_HOME" ]; then
+        die "KARAF_HOME is not valid: $KARAF_HOME"
+    fi
+}
+
+locateBase() {
+    if [ "x$KARAF_BASE" != "x" ]; then
+        if [ ! -d "$KARAF_BASE" ]; then
+            die "KARAF_BASE is not valid: $KARAF_BASE"
+        fi
+    else
+        KARAF_BASE=$KARAF_HOME
+    fi
+}
+
+setupNativePath() {
+    # Support for loading native libraries
+    LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:$KARAF_BASE/lib:$KARAF_HOME/lib"
+
+    # For Cygwin, set PATH from LD_LIBRARY_PATH
+    if $cygwin; then
+        LD_LIBRARY_PATH=`cygpath --path --windows "$LD_LIBRARY_PATH"`
+        PATH="$PATH;$LD_LIBRARY_PATH"
+        export PATH
+    fi
+    export LD_LIBRARY_PATH
+}
+
+locateJava() {
+    # Setup the Java Virtual Machine
+    if $cygwin ; then
+        [ -n "$JAVA" ] && JAVA=`cygpath --unix "$JAVA"`
+        [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+    fi
+
+    if [ "x$JAVA" = "x" ]; then
+        if [ "x$JAVA_HOME" = "x" ] && [ "$darwin" = "true" ]; then
+            JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Home
+        fi
+        if [ "x$JAVA_HOME" != "x" ]; then
+            if [ ! -d "$JAVA_HOME" ]; then
+                die "JAVA_HOME is not valid: $JAVA_HOME"
+            fi
+            JAVA="$JAVA_HOME/bin/java"
+        else
+            warn "JAVA_HOME not set; results may vary"
+            JAVA="java"
+        fi
+    fi
+}
+
+detectJVM() {
+   #echo "`$JAVA -version`"
+   # This service should call `java -version`,
+   # read stdout, and look for hints
+   if $JAVA -version 2>&1 | grep "^IBM" ; then
+       JVM_VENDOR="IBM"
+   # on OS/400, java -version does not contain IBM explicitly
+   elif $os400; then
+       JVM_VENDOR="IBM"
+   else
+       JVM_VENDOR="SUN"
+   fi
+   # echo "JVM vendor is $JVM_VENDOR"
+}
+
+setupDebugOptions() {
+    if [ "x$JAVA_OPTS" = "x" ]; then
+        JAVA_OPTS="$DEFAULT_JAVA_OPTS"
+    fi
+    export JAVA_OPTS
+
+    # Set Debug options if enabled
+    if [ "x$KARAF_DEBUG" != "x" ]; then
+        # Use the defaults if JAVA_DEBUG_OPTS was not set
+        if [ "x$JAVA_DEBUG_OPTS" = "x" ]; then
+            JAVA_DEBUG_OPTS="$DEFAULT_JAVA_DEBUG_OPTS"
+        fi
+
+        JAVA_OPTS="$JAVA_DEBUG_OPTS $JAVA_OPTS"
+        warn "Enabling Java debug options: $JAVA_DEBUG_OPTS"
+    fi
+}
+
+setupDefaults() {
+    DEFAULT_JAVA_OPTS="-Dstorage.file=${KARAF_HOME}/etc/instances.properties -Xms$JAVA_MIN_MEM -Xmx$JAVA_MAX_MEM "
+
+    #Set the JVM_VENDOR specific JVM flags
+    if [ "$JVM_VENDOR" = "SUN" ]; then
+        DEFAULT_JAVA_OPTS="-server $DEFAULT_JAVA_OPTS -Dcom.sun.management.jmxremote"
+    elif [ "$JVM_VENDOR" = "IBM" ]; then
+        if $os400; then
+            DEFAULT_JAVA_OPTS="$DEFAULT_JAVA_OPTS"
+        elif $aix; then
+            DEFAULT_JAVA_OPTS="-Xverify:none -Xlp $DEFAULT_JAVA_OPTS"
+        else
+            DEFAULT_JAVA_OPTS="-Xverify:none $DEFAULT_JAVA_OPTS"
+        fi
+    fi
+
+    # Add the jars in the lib dir
+    for file in $KARAF_HOME/lib/*.jar
+    do
+        if [ -z "$CLASSPATH" ]; then
+            CLASSPATH="$file"
+        else
+            CLASSPATH="$CLASSPATH:$file"
+        fi
+    done
+    DEFAULT_JAVA_DEBUG_OPTS="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005"
+
+    ##
+    ## TODO: Move to conf/profiler/yourkit.{sh|cmd}
+    ##
+    # Uncomment to enable YourKit profiling
+    #DEFAULT_JAVA_DEBUG_OPTS="-Xrunyjpagent"
+}
+
+init() {
+    # Determine if there is special OS handling we must perform
+    detectOS
+
+    # Unlimit the number of file descriptors if possible
+    unlimitFD
+
+    # Locate the Karaf home directory
+    locateHome
+
+    # Locate the Karaf base directory
+    locateBase
+
+    # Setup the native library path
+    setupNativePath
+
+    # Locate the Java VM to execute
+    locateJava
+
+    # Determine the JVM vendor
+    detectJVM
+
+    # Setup default options
+    setupDefaults
+
+    # Install debug options
+    setupDebugOptions
+
+}
+
+run() {
+
+    if $cygwin; then
+        KARAF_HOME=`cygpath --path --windows "$KARAF_HOME"`
+        KARAF_BASE=`cygpath --path --windows "$KARAF_BASE"`
+        CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+    fi
+
+    CLASSPATH=${KARAF_HOME}/system/org/apache/felix/karaf/gshell/org.apache.felix.karaf.gshell.admin/${project.version}/org.apache.felix.karaf.gshell.admin-${project.version}.jar:${KARAF_HOME}/system/org/apache/felix/karaf/gshell/org.apache.felix.karaf.gshell.console/${project.version}/org.apache.felix.karaf.gshell.console-${project.version}.jar:${KARAF_HOME}/system/org/apache/felix/gogo/org.apache.felix.gogo.runtime/${felix.gogo.version}/org.apache.felix.gogo.runtime-${felix.gogo.version}.jar:${KARAF_HOME}/system/org/apache/geronimo/blueprint-bundle/${geronimo.blueprint.version}/blueprint-bundle-${geronimo.blueprint.version}.jar:${KARAF_HOME}/system/org/ops4j/pax/logging/pax-logging-api/${pax.logging.version}/pax-logging-api-${pax.logging.version}.jar:${KARAF_HOME}/system/org/apache/felix/org.apache.felix.framework/${felix.framework.version}/org.apache.felix.framework-${felix.framework.version}.jar
+
+    exec $JAVA $JAVA_OPTS -Dkaraf.home="$KARAF_HOME" -Dkaraf.base="$KARAF_BASE" -Djava.util.logging.config.file=$KARAF_BASE/etc/java.util.logging.properties $OPTS -classpath "$CLASSPATH" org.apache.felix.karaf.gshell.admin.main.Execute "$@"
+}
+
+main() {
+    init
+    run "$@"
+}
+
+main "$@"
diff --git a/karaf/assembly/src/main/filtered-resources/bin/admin.bat b/karaf/assembly/src/main/filtered-resources/bin/admin.bat
new file mode 100644
index 0000000..d6458f0
--- /dev/null
+++ b/karaf/assembly/src/main/filtered-resources/bin/admin.bat
@@ -0,0 +1,97 @@
+@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 
+
+if not "%ECHO%" == "" echo %ECHO%
+
+setlocal
+set DIRNAME=%~dp0%
+set PROGNAME=%~nx0%
+set ARGS=%*
+
+goto BEGIN
+
+:warn
+    echo %PROGNAME%: %*
+goto :EOF
+
+:BEGIN
+
+rem # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+if not "%KARAF_HOME%" == "" (
+    call :warn Ignoring predefined value for KARAF_HOME
+)
+set KARAF_HOME=%DIRNAME%..
+if not exist "%KARAF_HOME%" (
+    call :warn KARAF_HOME is not valid: %KARAF_HOME%
+    goto END
+)
+
+if not "%KARAF_BASE%" == "" (
+    if not exist "%KARAF_BASE%" (
+       call :warn KARAF_BASE is not valid: %KARAF_BASE%
+       goto END
+    )
+)
+if "%KARAF_BASE%" == "" (
+  set KARAF_BASE=%KARAF_HOME%
+)
+
+set DEFAULT_JAVA_OPTS=-Dstorage.file="%KARAF_HOME%\etc\instances.properties"
+set DEFAULT_JAVA_DEBUG_OPTS=-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005
+
+rem Support for loading native libraries
+set PATH=%PATH%;%KARAF_BASE%\lib;%KARAF_HOME%\lib
+
+rem Setup the Java Virtual Machine
+if not "%JAVA%" == "" goto :Check_JAVA_END
+    set JAVA=java
+    if "%JAVA_HOME%" == "" call :warn JAVA_HOME not set; results may vary
+    if not "%JAVA_HOME%" == "" set JAVA=%JAVA_HOME%\bin\java
+    if not exist "%JAVA_HOME%" (
+        call :warn JAVA_HOME is not valid: "%JAVA_HOME%"
+        goto END
+    )
+:Check_JAVA_END
+
+if "%JAVA_OPTS%" == "" set JAVA_OPTS=%DEFAULT_JAVA_OPTS%
+
+if "%KARAF_DEBUG%" == "" goto :KARAF_DEBUG_END
+    rem Use the defaults if JAVA_DEBUG_OPTS was not set
+    if "%JAVA_DEBUG_OPTS%" == "" set JAVA_DEBUG_OPTS=%DEFAULT_JAVA_DEBUG_OPTS%
+    
+    set "JAVA_OPTS=%JAVA_DEBUG_OPTS% %JAVA_OPTS%"
+    call :warn Enabling Java debug options: %JAVA_DEBUG_OPTS%
+:KARAF_DEBUG_END
+
+set CLASSPATH=%KARAF_HOME%\system\org\apache\felix\karaf\gshell\org.apache.felix.karaf.gshell.admin\${project.version}\org.apache.felix.karaf.gshell.admin-${project.version}.jar;%KARAF_HOME%\system\org\apache\felix\karaf\gshell\org.apache.felix.karaf.gshell.console\${project.version}\org.apache.felix.karaf.gshell.console-${project.version}.jar;%KARAF_HOME%\system\org\apache\felix\gogo\org.apache.felix.gogo.runtime\${felix.gogo.version}\org.apache.felix.gogo.runtime-${felix.gogo.version}.jar;%KARAF_HOME%\system\org\apache\geronimo\blueprint-bundle\${geronimo.blueprint.version}\blueprint-bundle-${geronimo.blueprint.version}.jar;%KARAF_HOME%\system\org\ops4j\pax\logging\pax-logging-api\${pax.logging.version}\pax-logging-api-${pax.logging.version}.jar;%KARAF_HOME%\system\org\apache\felix\org.apache.felix.framework\${felix.framework.version}\org.apache.felix.framework-${felix.framework.version}.jar
+
+:EXECUTE
+    if "%SHIFT%" == "true" SET ARGS=%2 %3 %4 %5 %6 %7 %8
+    if not "%SHIFT%" == "true" SET ARGS=%1 %2 %3 %4 %5 %6 %7 %8    
+    rem Execute the Java Virtual Machine
+    "%JAVA%" %JAVA_OPTS% %OPTS% -classpath "%CLASSPATH%" -Dkaraf.home="%KARAF_HOME%" -Dkaraf.base="%KARAF_BASE%" -Djava.util.logging.config.file=%KARAF_BASE%\etc\java.util.logging.properties org.apache.felix.karaf.gshell.admin.main.Execute %ARGS%
+
+rem # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+:END
+
+endlocal
+
diff --git a/karaf/gshell/gshell-admin/pom.xml b/karaf/gshell/gshell-admin/pom.xml
index 4c6b459..c0c1300 100644
--- a/karaf/gshell/gshell-admin/pom.xml
+++ b/karaf/gshell/gshell-admin/pom.xml
@@ -56,10 +56,23 @@
         </dependency>
 
         <dependency>
+            <groupId>org.ops4j.pax.logging</groupId>
+            <artifactId>pax-logging-api</artifactId>
+            <scope>test</scope>
+        </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>easymockclassextension</artifactId>
+            <version>${easymock.version}</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/felix/karaf/gshell/admin/internal/AdminServiceImpl.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/felix/karaf/gshell/admin/internal/AdminServiceImpl.java
index 5f7ea9f..c9caca8 100644
--- a/karaf/gshell/gshell-admin/src/main/java/org/apache/felix/karaf/gshell/admin/internal/AdminServiceImpl.java
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/felix/karaf/gshell/admin/internal/AdminServiceImpl.java
@@ -148,10 +148,13 @@
         copyFilteredResourceToDir(serviceMixBase, "etc/system.properties", props);
         copyFilteredResourceToDir(serviceMixBase, "etc/org.apache.felix.karaf.shell.cfg", props);
         if( System.getProperty("os.name").startsWith("Win") ) {
-            copyFilteredResourceToDir(serviceMixBase, "bin/karaf.bat", props);
+            copyFilteredResourceToDir(serviceMixBase, "bin/start.bat", props);
+            copyFilteredResourceToDir(serviceMixBase, "bin/stop.bat", props);
         } else {
-            copyFilteredResourceToDir(serviceMixBase, "bin/karaf", props);
-            chmod(new File(serviceMixBase, "bin/karaf"), "a+x");
+            copyFilteredResourceToDir(serviceMixBase, "bin/start", props);
+            copyFilteredResourceToDir(serviceMixBase, "bin/stop", props);
+            chmod(new File(serviceMixBase, "bin/start"), "a+x");
+            chmod(new File(serviceMixBase, "bin/stop"), "a+x");
         }
         Instance instance = new InstanceImpl(this, name, serviceMixBase.toString());
         instances.put(name, instance);
@@ -188,7 +191,7 @@
         File outFile = new File(target, resource);
         if( !outFile.exists() ) {
             println(Ansi.ansi().a("Creating file: ").a(Ansi.Attribute.INTENSITY_BOLD).a(outFile.getPath()).a(Ansi.Attribute.RESET).toString());
-            InputStream is = getClass().getClassLoader().getResourceAsStream("/org/apache/felix/karaf/gshell/admin/" + resource);
+            InputStream is = getClass().getClassLoader().getResourceAsStream("org/apache/felix/karaf/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.
@@ -228,7 +231,7 @@
         File outFile = new File(target, resource);
         if( !outFile.exists() ) {
             println(Ansi.ansi().a("Creating file: ").a(Ansi.Attribute.INTENSITY_BOLD).a(outFile.getPath()).a(Ansi.Attribute.RESET).toString());
-            InputStream is = getClass().getClassLoader().getResourceAsStream("/org/apache/felix/karaf/gshell/admin/" + resource);
+            InputStream is = getClass().getClassLoader().getResourceAsStream("org/apache/felix/karaf/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));
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/felix/karaf/gshell/admin/internal/InstanceImpl.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/felix/karaf/gshell/admin/internal/InstanceImpl.java
index 0d22a08..873141f 100644
--- a/karaf/gshell/gshell-admin/src/main/java/org/apache/felix/karaf/gshell/admin/internal/InstanceImpl.java
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/felix/karaf/gshell/admin/internal/InstanceImpl.java
@@ -147,7 +147,7 @@
                 + " -Dkaraf.startRemoteShell=true"
                 + " -classpath " + classpath.toString()
                 + " org.apache.felix.karaf.main.Bootstrap";
-        LOG.debug("Starting instance with command: " + command);
+        LOG.debug("Starting instance " + name + " with command: " + command);
         this.process = ProcessBuilderFactory.newInstance().newBuilder()
                         .directory(new File(location))
                         .command(command)
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/felix/karaf/gshell/admin/main/Execute.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/felix/karaf/gshell/admin/main/Execute.java
new file mode 100644
index 0000000..f970ce2
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/felix/karaf/gshell/admin/main/Execute.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.karaf.gshell.admin.main;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.felix.gogo.commands.Action;
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.basic.DefaultActionPreparator;
+import org.apache.felix.karaf.gshell.admin.internal.AdminServiceImpl;
+import org.apache.felix.karaf.gshell.admin.internal.commands.AdminCommandSupport;
+import org.apache.felix.karaf.gshell.admin.internal.commands.CreateCommand;
+import org.apache.felix.karaf.gshell.admin.internal.commands.DestroyCommand;
+import org.apache.felix.karaf.gshell.admin.internal.commands.ListCommand;
+import org.apache.felix.karaf.gshell.admin.internal.commands.StartCommand;
+import org.apache.felix.karaf.gshell.admin.internal.commands.StopCommand;
+import org.apache.felix.karaf.gshell.admin.internal.commands.ChangePortCommand;
+import org.fusesource.jansi.AnsiConsole;
+
+public class Execute {
+    static Class<? extends Action> x = CreateCommand.class;
+    private static final Class<?> [] COMMAND_CLASSES = new Class[] {
+        CreateCommand.class,
+        StartCommand.class,
+        StopCommand.class,
+        DestroyCommand.class,  
+        ListCommand.class,
+        ChangePortCommand.class};
+    private static final Map<String, Class<?>> COMMANDS = new TreeMap<String, Class<?>>();
+    static {
+        for (Class<?> c : COMMAND_CLASSES) {
+            Command ann = c.getAnnotation(Command.class);
+            if (ann == null) {
+                continue;
+            }
+            COMMANDS.put(ann.name(), c);
+        }
+    }    
+    
+    // For testing
+    static boolean exitAllowed = true;
+
+    public static void main(String[] args) throws Exception {
+        AnsiConsole.systemInstall();
+
+        if (args.length == 0) {
+            listCommands();
+            exit(0);
+        }
+        
+        Class<?> cls = COMMANDS.get(args[0]);
+        if (cls == null) {
+            System.err.println("Command not found: " + args[0]);
+            exit(-1);
+        }
+
+        String storage = System.getProperty("storage.file");
+        if (storage == null) {
+            System.err.println("System property 'storage.file' is not set. \n" +
+        		"This property needs to be set to the full path of the instance.properties file.");
+            exit(-1);
+        }
+        File storageFile = new File(storage);
+        System.setProperty("user.dir", storageFile.getParentFile().getParentFile().getCanonicalPath());
+        
+        Object command = cls.newInstance();
+        if (command instanceof AdminCommandSupport) {
+            execute((AdminCommandSupport) command, storageFile, args);            
+        } else {
+            System.err.println("Not an admin command: " + args[0]);
+            exit(-1);
+        }
+    }
+    
+    static void execute(AdminCommandSupport command, File storageFile, String[] args) throws Exception {
+        DefaultActionPreparator dap = new DefaultActionPreparator();
+        List<Object> params = new ArrayList<Object>(Arrays.asList(args));
+        params.remove(0); // this is the actual command name
+
+        if (!dap.prepare(command, null, params)) {
+            return;
+        }
+                
+        AdminServiceImpl admin = new AdminServiceImpl();
+        admin.setStorageLocation(storageFile);
+        admin.init();
+        command.setAdminService(admin);
+        command.execute(null);
+    }
+
+    private static void listCommands() {
+        System.out.println("Available commands:");
+        for (Map.Entry<String, Class<?>> entry : COMMANDS.entrySet()) {
+            Command ann = entry.getValue().getAnnotation(Command.class);
+            System.out.printf("  %s - %s\n", entry.getKey(), ann.description());
+        }
+        
+        System.out.println("Type 'command --help' for more help on the specified command.");
+    }
+
+    private static void exit(int rc) {
+        if (exitAllowed) {
+            System.exit(rc);
+        } else {
+            throw new RuntimeException("" + rc);
+        }
+    }
+}
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/felix/karaf/gshell/admin/bin/karaf b/karaf/gshell/gshell-admin/src/main/resources/org/apache/felix/karaf/gshell/admin/bin/start
similarity index 92%
rename from karaf/gshell/gshell-admin/src/main/resources/org/apache/felix/karaf/gshell/admin/bin/karaf
rename to karaf/gshell/gshell-admin/src/main/resources/org/apache/felix/karaf/gshell/admin/bin/start
index 180092b..383845f 100644
--- a/karaf/gshell/gshell-admin/src/main/resources/org/apache/felix/karaf/gshell/admin/bin/karaf
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/felix/karaf/gshell/admin/bin/start
@@ -19,7 +19,6 @@
 ################################################################################
 
 KARAF_HOME=${karaf.home}
-KARAF_BASE=${karaf.base}
 
-export KARAF_BASE
-${KARAF_HOME}/bin/karaf "$*"
+${KARAF_HOME}/bin/admin start ${karaf.name} "$@"
+
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/felix/karaf/gshell/admin/bin/karaf.bat b/karaf/gshell/gshell-admin/src/main/resources/org/apache/felix/karaf/gshell/admin/bin/start.bat
similarity index 93%
copy from karaf/gshell/gshell-admin/src/main/resources/org/apache/felix/karaf/gshell/admin/bin/karaf.bat
copy to karaf/gshell/gshell-admin/src/main/resources/org/apache/felix/karaf/gshell/admin/bin/start.bat
index 1358ba7..2da0758 100644
--- a/karaf/gshell/gshell-admin/src/main/resources/org/apache/felix/karaf/gshell/admin/bin/karaf.bat
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/felix/karaf/gshell/admin/bin/start.bat
@@ -18,8 +18,6 @@
 REM 
 REM =========================================================================
 
-SET KARAF_BASE=${karaf.base}
-SETLOCAL
 SET KARAF_HOME=${karaf.home}
 
-%KARAF_HOME%\bin\karaf.bat %*
+%KARAF_HOME%\bin\admin.bat start ${karaf.name}
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/felix/karaf/gshell/admin/bin/karaf b/karaf/gshell/gshell-admin/src/main/resources/org/apache/felix/karaf/gshell/admin/bin/stop
similarity index 92%
copy from karaf/gshell/gshell-admin/src/main/resources/org/apache/felix/karaf/gshell/admin/bin/karaf
copy to karaf/gshell/gshell-admin/src/main/resources/org/apache/felix/karaf/gshell/admin/bin/stop
index 180092b..1fbfc35 100644
--- a/karaf/gshell/gshell-admin/src/main/resources/org/apache/felix/karaf/gshell/admin/bin/karaf
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/felix/karaf/gshell/admin/bin/stop
@@ -19,7 +19,6 @@
 ################################################################################
 
 KARAF_HOME=${karaf.home}
-KARAF_BASE=${karaf.base}
 
-export KARAF_BASE
-${KARAF_HOME}/bin/karaf "$*"
+${KARAF_HOME}/bin/admin stop ${karaf.name} "$@"
+
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/felix/karaf/gshell/admin/bin/karaf.bat b/karaf/gshell/gshell-admin/src/main/resources/org/apache/felix/karaf/gshell/admin/bin/stop.bat
similarity index 93%
rename from karaf/gshell/gshell-admin/src/main/resources/org/apache/felix/karaf/gshell/admin/bin/karaf.bat
rename to karaf/gshell/gshell-admin/src/main/resources/org/apache/felix/karaf/gshell/admin/bin/stop.bat
index 1358ba7..fd6279e 100644
--- a/karaf/gshell/gshell-admin/src/main/resources/org/apache/felix/karaf/gshell/admin/bin/karaf.bat
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/felix/karaf/gshell/admin/bin/stop.bat
@@ -18,8 +18,6 @@
 REM 
 REM =========================================================================
 
-SET KARAF_BASE=${karaf.base}
-SETLOCAL
 SET KARAF_HOME=${karaf.home}
 
-%KARAF_HOME%\bin\karaf.bat %*
+%KARAF_HOME%\bin\admin.bat stop ${karaf.name}
diff --git a/karaf/gshell/gshell-admin/src/test/java/org/apache/felix/karaf/gshell/admin/main/ExecuteTest.java b/karaf/gshell/gshell-admin/src/test/java/org/apache/felix/karaf/gshell/admin/main/ExecuteTest.java
new file mode 100644
index 0000000..5b96fba
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/test/java/org/apache/felix/karaf/gshell/admin/main/ExecuteTest.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.karaf.gshell.admin.main;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import junit.framework.TestCase;
+
+import org.apache.felix.karaf.gshell.admin.AdminService;
+import org.apache.felix.karaf.gshell.admin.internal.AdminServiceImpl;
+import org.apache.felix.karaf.gshell.admin.internal.commands.AdminCommandSupport;
+import org.easymock.IAnswer;
+import org.easymock.classextension.EasyMock;
+
+public class ExecuteTest extends TestCase {
+    private String userDir;
+    
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        Execute.exitAllowed = false;
+        userDir = System.getProperty("user.dir");
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        Execute.exitAllowed = true;
+        System.setProperty("user.dir", userDir);
+    }
+
+    public void testListCommands() throws Exception {
+        PrintStream oldOut = System.out;
+        
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        PrintStream capturedOut = new PrintStream(baos); 
+        System.setOut(capturedOut);
+
+        try {
+            Execute.main(new String [] {});            
+        } catch (RuntimeException re) {
+            assertEquals("0", re.getMessage());
+
+            String s = new String(baos.toByteArray());            
+            assertTrue(s.contains("list"));
+            assertTrue(s.contains("create"));
+            assertTrue(s.contains("destroy"));
+        } finally {
+            System.setOut(oldOut);
+        }
+    }
+    
+    public void testNonexistingCommand() throws Exception {
+        try {
+            Execute.main(new String [] {"bheuaark"});
+        } catch (RuntimeException re) {
+            assertEquals("-1", re.getMessage());
+        }
+    }
+    
+    public void testNoStorageFile() throws Exception {
+        PrintStream oldErr = System.err;
+        
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        PrintStream capturedErr = new PrintStream(baos); 
+        System.setErr(capturedErr);
+
+        try {
+            Execute.main(new String [] {"create"});            
+        } catch (RuntimeException re) {
+            assertEquals("-1", re.getMessage());
+            
+            String s = new String(baos.toByteArray());            
+            assertTrue(s.contains("storage.file"));
+            assertTrue(s.contains("instance.properties"));
+        } finally {
+            System.setErr(oldErr);
+        } 
+    }
+    
+    public void testSetDir() throws Exception {
+        Properties oldProps = (Properties) System.getProperties().clone();
+        File tempFile = File.createTempFile(getName(), ".tmp");
+        assertFalse("Precondition failed", 
+            tempFile.getParentFile().getParentFile().getCanonicalPath().equals(System.getProperty("user.dir")));
+
+        System.setProperty("storage.file", tempFile.getCanonicalPath());
+        try {
+            Execute.main(new String [] {"list"});            
+            assertTrue(tempFile.getParentFile().getParentFile().getCanonicalPath().equals(System.getProperty("user.dir")));
+        } finally {
+            System.setProperties(oldProps);
+            assertNull("Postcondition failed", System.getProperty("storage.file"));
+            tempFile.delete();
+        }        
+    }
+    
+    public void testExecute() throws Exception {
+        final File tempFile = File.createTempFile(getName(), ".properties");
+        Properties p = new Properties();
+        p.setProperty("port", "1302");
+        FileOutputStream fos = new FileOutputStream(tempFile);
+        p.store(fos, "");
+        fos.close();
+
+        final List<AdminServiceImpl> admins = new ArrayList<AdminServiceImpl>();
+        try {
+            AdminCommandSupport mockCommand = EasyMock.createStrictMock(AdminCommandSupport.class);
+            mockCommand.setAdminService((AdminService) EasyMock.anyObject());
+            EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
+                public Object answer() throws Throwable {
+                    AdminServiceImpl svc = (AdminServiceImpl) EasyMock.getCurrentArguments()[0];
+                    assertEquals(tempFile, svc.getStorageLocation());
+                    admins.add(svc);
+                    return null;
+                }
+            });
+            
+            EasyMock.expect(mockCommand.execute(null)).andAnswer(new IAnswer<Object>() {
+                public Object answer() throws Throwable {
+                    // The Admin Service should be initialized at this point.
+                    // One way to find this out is by reading out the port number
+                    AdminServiceImpl admin = admins.get(0);
+                    Field field = AdminServiceImpl.class.getDeclaredField("defaultPortStart");
+                    field.setAccessible(true);
+                    assertEquals(1302, field.get(admin));
+                    return null;
+                }
+            });
+            EasyMock.replay(mockCommand);            
+            
+            Execute.execute(mockCommand, tempFile, new String [] {"test"});
+            
+            EasyMock.verify(mockCommand);
+        } finally {
+            tempFile.delete();
+        }
+    }
+}