You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gn...@apache.org on 2014/09/11 23:19:19 UTC

[1/2] git commit: Fix packaging problems with gogo as Gogo is deployed in its own bundle, but also partially embedded in shell/core. * Embed gogo in shell/core * Export the secured CommandProcessor directly

Repository: karaf
Updated Branches:
  refs/heads/karaf-2.x fda3393cf -> bf0311611


Fix packaging problems with gogo as Gogo is deployed in its own bundle, but also partially embedded in shell/core.
 * Embed gogo in shell/core
 * Export the secured CommandProcessor directly


Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/94ffa5e5
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/94ffa5e5
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/94ffa5e5

Branch: refs/heads/karaf-2.x
Commit: 94ffa5e5bedff9bb779ddf48888aea351459e006
Parents: fda3393
Author: Guillaume Nodet <gn...@gmail.com>
Authored: Thu Sep 11 23:04:19 2014 +0200
Committer: Guillaume Nodet <gn...@gmail.com>
Committed: Thu Sep 11 23:19:09 2014 +0200

----------------------------------------------------------------------
 .../standard/src/main/resources/features.xml    |   1 -
 .../apache/karaf/itests/KarafTestSupport.java   |   4 +-
 shell/console/pom.xml                           |  10 +-
 .../karaf/shell/console/jline/Console.java      | 107 +---------
 .../shell/console/jline/ConsoleFactory.java     |   8 +-
 .../security/impl/SecCommandProcessorImpl.java  |  42 ++++
 .../impl/SecuredCommandProcessorImpl.java       |  99 +++++-----
 .../OSGI-INF/blueprint/karaf-console.xml        |  25 +--
 .../org/apache/felix/gogo/commands/Context.java |   2 +-
 .../karaf/shell/console/jline/ConsoleTest.java  | 195 -------------------
 .../impl/SecuredCommandProcessorImplTest.java   |   2 +-
 .../karaf/shell/ssh/ShellFactoryImpl.java       | 112 ++++++-----
 12 files changed, 185 insertions(+), 422 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/94ffa5e5/assemblies/features/standard/src/main/resources/features.xml
----------------------------------------------------------------------
diff --git a/assemblies/features/standard/src/main/resources/features.xml b/assemblies/features/standard/src/main/resources/features.xml
index 587bd1d..99008d4 100644
--- a/assemblies/features/standard/src/main/resources/features.xml
+++ b/assemblies/features/standard/src/main/resources/features.xml
@@ -99,7 +99,6 @@
     <feature name="shell" description="Karaf Shell" version="${project.version}">
         <feature>aries-blueprint</feature>
         <feature>jaas</feature>
-        <bundle start-level="30">mvn:org.apache.felix/org.apache.felix.gogo.runtime/${felix.gogo.version}</bundle>
         <bundle start-level="30">mvn:org.apache.karaf.shell/org.apache.karaf.shell.console/${project.version}</bundle>
         <bundle start-level="30">mvn:org.apache.karaf.shell/org.apache.karaf.shell.commands/${project.version}</bundle>
         <bundle start-level="30">mvn:org.apache.karaf.shell/org.apache.karaf.shell.osgi/${project.version}</bundle>

http://git-wip-us.apache.org/repos/asf/karaf/blob/94ffa5e5/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java
----------------------------------------------------------------------
diff --git a/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java b/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java
index cbba8b7..c9321a5 100644
--- a/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java
+++ b/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java
@@ -120,8 +120,6 @@ public class KarafTestSupport {
         String response;
         final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
         final PrintStream printStream = new PrintStream(byteArrayOutputStream);
-        final CommandProcessor commandProcessor = getOsgiService(CommandProcessor.class);
-        final CommandSession commandSession = commandProcessor.createSession(System.in, printStream, System.err);
         final Callable<String> commandCallable = new Callable<String>() {
             @Override
             public String call() throws Exception {
@@ -129,6 +127,8 @@ public class KarafTestSupport {
                     if (!silent) {
                         System.err.println(command);
                     }
+                    final CommandProcessor commandProcessor = getOsgiService(CommandProcessor.class);
+                    final CommandSession commandSession = commandProcessor.createSession(System.in, printStream, System.err);
                     commandSession.execute(command);
                 } catch (Exception e) {
                     throw new RuntimeException(e.getMessage(), e);

http://git-wip-us.apache.org/repos/asf/karaf/blob/94ffa5e5/shell/console/pom.xml
----------------------------------------------------------------------
diff --git a/shell/console/pom.xml b/shell/console/pom.xml
index a2ebe1f..4a8d98f 100644
--- a/shell/console/pom.xml
+++ b/shell/console/pom.xml
@@ -119,15 +119,16 @@
                 <configuration>
                     <instructions>
                         <Import-Package>
-                            !org.apache.felix.gogo.runtime.*,
                             org.osgi.service.event;resolution:=optional,
                             org.apache.karaf.branding;resolution:=optional,
                             org.apache.sshd.agent*;resolution:=optional,
                             *
                         </Import-Package>
                         <Export-Package>
-                            org.apache.felix.gogo.runtime.*;version=${felix.gogo.version};-split-package:=merge-first,
-                            org.apache.felix.gogo.commands.*;version=${felix.gogo.version};-split-package:=merge-first,
+                            org.apache.felix.service.command;version=${felix.gogo.version};-split-package:=merge-first;-noimport:=true,
+                            org.apache.felix.service.threadio;version=${felix.gogo.version};-split-package:=merge-first;-noimport:=true,
+                            org.apache.felix.gogo.api;version=${felix.gogo.version};-split-package:=merge-first;-noimport:=true,
+                            org.apache.felix.gogo.commands.*;version=${felix.gogo.version};-split-package:=merge-first;-noimport:=true,
                             org.apache.karaf.shell.console*;version=${project.version},
                             org.fusesource.jansi;version=${jansi.version};-split-package:=merge-first,
                             jline*;version=${jline.version},
@@ -135,6 +136,7 @@
                             org.fusesource.hawtjni*;version=1.0;-split-package:=merge-first
                         </Export-Package>
                         <Private-Package>
+                            org.apache.felix.gogo.runtime.*,
                             org.apache.karaf.shell.security.impl,
                             org.fusesource.jansi.internal;-split-package:=merge-first,
                             !org.apache.karaf.util.properties,
@@ -155,13 +157,11 @@
                         <Main-Class>
                             org.apache.karaf.shell.console.Main
                         </Main-Class>
-                        <!--
                         <Export-Service>
                             org.apache.felix.service.threadio.ThreadIO,
                             org.apache.felix.service.command.CommandProcessor,
                             org.apache.felix.service.command.Converter
                         </Export-Service>
-                        -->
                     </instructions>
                     <unpackBundle>true</unpackBundle>
                 </configuration>

http://git-wip-us.apache.org/repos/asf/karaf/blob/94ffa5e5/shell/console/src/main/java/org/apache/karaf/shell/console/jline/Console.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/jline/Console.java b/shell/console/src/main/java/org/apache/karaf/shell/console/jline/Console.java
index ac48e0c..4ab38c6 100644
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/jline/Console.java
+++ b/shell/console/src/main/java/org/apache/karaf/shell/console/jline/Console.java
@@ -102,7 +102,8 @@ public class Console implements Runnable
         this.queue = new ArrayBlockingQueue<Integer>(1024);
         this.terminal = term == null ? new UnsupportedTerminal() : term;
         this.consoleInput = new ConsoleInputStream();
-        this.session = new DelegateSession();
+
+        this.session = processor.createSession(consoleInput, out, err);
         this.session.put("SCOPE", "shell:osgi:*");
         this.closeCallback = closeCallback;
         this.bundleContext = bc;
@@ -178,7 +179,6 @@ public class Console implements Runnable
     {
         try {
             threadIO.setStreams(consoleInput, out, err);
-            SecuredCommandProcessorImpl secCP = createSecuredCommandProcessor();
             thread = Thread.currentThread();
             CommandSessionHolder.setSession(session);
             running = true;
@@ -212,11 +212,6 @@ public class Console implements Runnable
                     logException(t);
                 }
             }
-            try {
-                secCP.close();
-            } catch (Throwable t) {
-                // Ignore
-            }
             close(true);
         } finally {
             try {
@@ -227,22 +222,6 @@ public class Console implements Runnable
         }
     }
     
-    SecuredCommandProcessorImpl createSecuredCommandProcessor() {
-        if (!(session instanceof DelegateSession)) {
-            throw new IllegalStateException("Should be an Delegate Session here, about to set the delegate");
-        }
-        DelegateSession is = (DelegateSession)session;
-
-        // make it active
-        SecuredCommandProcessorImpl secCP = new SecuredCommandProcessorImpl(bundleContext);
-        CommandSession s = secCP.createSession(consoleInput, out, err);
-
-        // before the session is activated attributes may have been set on it. Pass these on to the real
-        // session now
-        is.setDelegate(s);
-        return secCP;
-    }
-
     private void logException(Throwable t) {
         try {
             boolean isCommandNotFound = "org.apache.felix.gogo.runtime.CommandNotFoundException".equals(t.getClass().getName());
@@ -443,88 +422,6 @@ public class Console implements Runnable
         thread.interrupt();
     }
 
-    static class DelegateSession implements CommandSession {
-        final Map<String, Object> attrs = new HashMap<String, Object>();
-        volatile CommandSession delegate;
-
-        @Override
-        public Object execute(CharSequence commandline) throws Exception {
-            if (delegate != null)
-                return delegate.execute(commandline);
-
-            throw new UnsupportedOperationException();
-        }
-
-        void setDelegate(CommandSession s) {
-            synchronized (this) {
-                for (Map.Entry<String, Object> entry : attrs.entrySet()) {
-                    s.put(entry.getKey(), entry.getValue());
-                }
-            }
-            delegate = s;
-        }
-
-        @Override
-        public void close() {
-            if (delegate != null)
-                delegate.close();
-        }
-
-        @Override
-        public InputStream getKeyboard() {
-            if (delegate != null)
-                return delegate.getKeyboard();
-
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public PrintStream getConsole() {
-            if (delegate != null)
-                return delegate.getConsole();
-
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public Object get(String name) {
-            if (delegate != null)
-                return delegate.get(name);
-
-            return attrs.get(name);
-        }
-
-        // you can put attributes on this session before it's delegate is set...
-        @Override
-        public void put(String name, Object value) {
-            if (delegate != null) {
-                delegate.put(name, value);
-                return;
-            }
-
-            // there is no delegate yet, so we'll keep the attributes locally
-            synchronized (this) {
-                attrs.put(name, value);
-            }
-        }
-
-        @Override
-        public CharSequence format(Object target, int level) {
-            if (delegate != null)
-                return delegate.format(target, level);
-
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public Object convert(Class<?> type, Object instance) {
-            if (delegate != null)
-                return delegate.convert(type, instance);
-
-            throw new UnsupportedOperationException();
-        }
-    }
-
     private class ConsoleInputStream extends InputStream
     {
         private int read(boolean wait) throws IOException

http://git-wip-us.apache.org/repos/asf/karaf/blob/94ffa5e5/shell/console/src/main/java/org/apache/karaf/shell/console/jline/ConsoleFactory.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/jline/ConsoleFactory.java b/shell/console/src/main/java/org/apache/karaf/shell/console/jline/ConsoleFactory.java
index 68cf181..fcae59f 100644
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/jline/ConsoleFactory.java
+++ b/shell/console/src/main/java/org/apache/karaf/shell/console/jline/ConsoleFactory.java
@@ -73,6 +73,10 @@ public class ConsoleFactory {
         this.bundleContext = bundleContext;
     }
 
+    public void setCommandProcessor(CommandProcessor commandProcessor) {
+        this.commandProcessor = commandProcessor;
+    }
+
     public void setThreadIO(ThreadIO threadIO) {
         this.threadIO = threadIO;
     }
@@ -95,7 +99,7 @@ public class ConsoleFactory {
         this.start = start;
     }
 
-    protected void start() throws Exception {
+    public void start() throws Exception {
         if (start) {
             Subject subject = new Subject();
             String userName = System.getProperty("karaf.local.user");
@@ -211,7 +215,7 @@ public class ConsoleFactory {
         return parts[0];
     }
 
-    protected void stop() throws Exception {
+    public void stop() throws Exception {
         if (registration != null) {
             registration.unregister();
         }

http://git-wip-us.apache.org/repos/asf/karaf/blob/94ffa5e5/shell/console/src/main/java/org/apache/karaf/shell/security/impl/SecCommandProcessorImpl.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/security/impl/SecCommandProcessorImpl.java b/shell/console/src/main/java/org/apache/karaf/shell/security/impl/SecCommandProcessorImpl.java
new file mode 100644
index 0000000..29b02a2
--- /dev/null
+++ b/shell/console/src/main/java/org/apache/karaf/shell/security/impl/SecCommandProcessorImpl.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.karaf.shell.security.impl;
+
+import java.io.InputStream;
+import java.io.PrintStream;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.felix.service.threadio.ThreadIO;
+import org.osgi.framework.BundleContext;
+
+public class SecCommandProcessorImpl implements CommandProcessor {
+
+    private final BundleContext bundleContext;
+    private final ThreadIO threadIO;
+
+    public SecCommandProcessorImpl(BundleContext bundleContext, ThreadIO threadIO) {
+        this.bundleContext = bundleContext;
+        this.threadIO = threadIO;
+    }
+
+    public CommandSession createSession(InputStream in, PrintStream out, PrintStream err) {
+        SecuredCommandProcessorImpl scp = new SecuredCommandProcessorImpl(bundleContext, threadIO);
+        return scp.createSession(in, out, err);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/94ffa5e5/shell/console/src/main/java/org/apache/karaf/shell/security/impl/SecuredCommandProcessorImpl.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/security/impl/SecuredCommandProcessorImpl.java b/shell/console/src/main/java/org/apache/karaf/shell/security/impl/SecuredCommandProcessorImpl.java
index 84224c4..3a83958 100644
--- a/shell/console/src/main/java/org/apache/karaf/shell/security/impl/SecuredCommandProcessorImpl.java
+++ b/shell/console/src/main/java/org/apache/karaf/shell/security/impl/SecuredCommandProcessorImpl.java
@@ -22,7 +22,6 @@ import org.apache.felix.gogo.runtime.CommandProxy;
 import org.apache.felix.gogo.runtime.activator.Activator;
 import org.apache.felix.service.command.CommandProcessor;
 import org.apache.felix.service.command.Converter;
-import org.apache.felix.service.command.Function;
 import org.apache.felix.service.threadio.ThreadIO;
 import org.apache.karaf.jaas.boot.principal.RolePrincipal;
 import org.osgi.framework.BundleContext;
@@ -35,48 +34,47 @@ import javax.security.auth.Subject;
 import java.security.AccessControlContext;
 import java.security.AccessController;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 
 public class SecuredCommandProcessorImpl extends CommandProcessorImpl {
 
     private final BundleContext bundleContext;
-    private final ServiceReference<ThreadIO> threadIOServiceReference;
     private final ServiceTracker<Object, Object> commandTracker;
     private final ServiceTracker<Converter, Converter> converterTracker;
     private final ServiceTracker<CommandSessionListener, CommandSessionListener> listenerTracker;
 
-    public SecuredCommandProcessorImpl(BundleContext bc) {
-        this(bc, bc.getServiceReference(ThreadIO.class));
-    }
-
-    private SecuredCommandProcessorImpl(BundleContext bc, ServiceReference<ThreadIO> sr) {
-        super(bc.getService(sr));
+    public SecuredCommandProcessorImpl(BundleContext bc, ThreadIO io) {
+        super(io);
         bundleContext = bc;
-        threadIOServiceReference = sr;
+
+        String roleClause = "";
 
         AccessControlContext acc = AccessController.getContext();
         Subject sub = Subject.getSubject(acc);
-        if (sub == null)
-            throw new SecurityException("No current Subject in the Access Control Context");
-
-        Set<RolePrincipal> rolePrincipals = sub.getPrincipals(RolePrincipal.class);
-        if (rolePrincipals.size() == 0)
-            throw new SecurityException("Current user has no associated roles.");
-
-        // TODO cater for custom roles
-        StringBuilder sb = new StringBuilder();
-        sb.append("(|");
-        for (RolePrincipal rp : rolePrincipals) {
-            sb.append('(');
-            sb.append("org.apache.karaf.service.guard.roles");
-            sb.append('=');
-            sb.append(rp.getName());
+        if (sub != null) {
+            Set<RolePrincipal> rolePrincipals = sub.getPrincipals(RolePrincipal.class);
+            if (rolePrincipals.size() == 0)
+                throw new SecurityException("Current user has no associated roles.");
+
+            // TODO cater for custom roles
+            StringBuilder sb = new StringBuilder();
+            sb.append("(|");
+            for (RolePrincipal rp : rolePrincipals) {
+                sb.append('(');
+                sb.append("org.apache.karaf.service.guard.roles");
+                sb.append('=');
+                sb.append(rp.getName());
+                sb.append(')');
+            }
+            sb.append("(!(org.apache.karaf.service.guard.roles=*))"); // Or no roles specified at all
             sb.append(')');
+            roleClause = sb.toString();
         }
-        sb.append("(!(org.apache.karaf.service.guard.roles=*))"); // Or no roles specified at all
-        sb.append(')');
-        String roleClause = sb.toString();
 
         addConstant(Activator.CONTEXT, bc);
         addCommand("osgi", this, "addCommand");
@@ -101,7 +99,6 @@ public class SecuredCommandProcessorImpl extends CommandProcessorImpl {
         commandTracker.close();
         converterTracker.close();
         listenerTracker.close();
-        bundleContext.ungetService(threadIOServiceReference);
     }
 
     private ServiceTracker<Object, Object> trackCommands(final BundleContext context, String roleClause) throws InvalidSyntaxException {
@@ -109,45 +106,57 @@ public class SecuredCommandProcessorImpl extends CommandProcessorImpl {
                 CommandProcessor.COMMAND_SCOPE, CommandProcessor.COMMAND_FUNCTION, roleClause));
 
         return new ServiceTracker<Object, Object>(context, filter, null) {
+            private final ConcurrentMap<ServiceReference, Map<String, CommandProxy>> proxies
+                    = new ConcurrentHashMap<ServiceReference, Map<String, CommandProxy>>();
+
             @Override
-            public Object addingService(ServiceReference<Object> reference) {
+            public Object addingService(ServiceReference reference)
+            {
                 Object scope = reference.getProperty(CommandProcessor.COMMAND_SCOPE);
                 Object function = reference.getProperty(CommandProcessor.COMMAND_FUNCTION);
                 List<Object> commands = new ArrayList<Object>();
 
-                if (scope != null && function != null) {
-                    if (function.getClass().isArray()) {
-                        for (Object f : ((Object[]) function)) {
-                            Function target = new CommandProxy(context, reference,
-                                    f.toString());
+                if (scope != null && function != null)
+                {
+                    Map<String, CommandProxy> proxyMap = new HashMap<String, CommandProxy>();
+                    if (function.getClass().isArray())
+                    {
+                        for (Object f : ((Object[]) function))
+                        {
+                            CommandProxy target = new CommandProxy(context, reference, f.toString());
+                            proxyMap.put(f.toString(), target);
                             addCommand(scope.toString(), target, f.toString());
                             commands.add(target);
                         }
-                    } else {
-                        Function target = new CommandProxy(context, reference,
-                                function.toString());
+                    }
+                    else
+                    {
+                        CommandProxy target = new CommandProxy(context, reference, function.toString());
+                        proxyMap.put(function.toString(), target);
                         addCommand(scope.toString(), target, function.toString());
                         commands.add(target);
                     }
+                    proxies.put(reference, proxyMap);
                     return commands;
                 }
                 return null;
             }
 
             @Override
-            public void removedService(ServiceReference<Object> reference, Object service) {
+            public void removedService(ServiceReference reference, Object service)
+            {
                 Object scope = reference.getProperty(CommandProcessor.COMMAND_SCOPE);
                 Object function = reference.getProperty(CommandProcessor.COMMAND_FUNCTION);
 
-                if (scope != null && function != null) {
-                    if (!function.getClass().isArray()) {
-                        removeCommand(scope.toString(), function.toString());
-                    } else {
-                        for (Object func : (Object[]) function) {
-                            removeCommand(scope.toString(), func.toString());
-                        }
+                if (scope != null && function != null)
+                {
+                    Map<String, CommandProxy> proxyMap = proxies.remove(reference);
+                    for (Map.Entry<String, CommandProxy> entry : proxyMap.entrySet())
+                    {
+                        removeCommand(scope.toString(), entry.getKey());
                     }
                 }
+
                 super.removedService(reference, service);
             }
         };

http://git-wip-us.apache.org/repos/asf/karaf/blob/94ffa5e5/shell/console/src/main/resources/OSGI-INF/blueprint/karaf-console.xml
----------------------------------------------------------------------
diff --git a/shell/console/src/main/resources/OSGI-INF/blueprint/karaf-console.xml b/shell/console/src/main/resources/OSGI-INF/blueprint/karaf-console.xml
index 65c1991..cf544bc 100644
--- a/shell/console/src/main/resources/OSGI-INF/blueprint/karaf-console.xml
+++ b/shell/console/src/main/resources/OSGI-INF/blueprint/karaf-console.xml
@@ -26,18 +26,23 @@
         </ext:default-properties>
     </ext:property-placeholder>
 
-    <reference id="commandProcessor" interface="org.apache.felix.service.command.CommandProcessor">
-        <reference-listener ref="consoleFactory"
-                            bind-method="registerCommandProcessor"
-                            unbind-method="unregisterCommandProcessor"/>
-    </reference>
-    <reference id="threadIO" interface="org.apache.felix.service.threadio.ThreadIO" />
-
-    <bean id="consoleFactory" class="org.apache.karaf.shell.console.jline.ConsoleFactory">
+    <bean id="threadIO" class="org.apache.felix.gogo.runtime.threadio.ThreadIOImpl"
+          init-method="start" destroy-method="stop"/>
+    <service ref="threadIO" auto-export="interfaces"/>
+
+    <bean id="commandProcessor" class="org.apache.karaf.shell.security.impl.SecCommandProcessorImpl">
+        <argument ref="blueprintBundleContext"/>
+        <argument ref="threadIO"/>
+    </bean>
+    <service ref="commandProcessor" auto-export="interfaces"/>
+
+    <bean id="consoleFactory" class="org.apache.karaf.shell.console.jline.ConsoleFactory"
+            init-method="start" destroy-method="stop">
         <property name="start" value="$[karaf.startLocalConsole]"/>
         <property name="bundleContext" ref="blueprintBundleContext"/>
         <property name="terminalFactory" ref="terminalFactory"/>
         <property name="threadIO" ref="threadIO"/>
+        <property name="commandProcessor" ref="commandProcessor"/>
     </bean>
 
     <bean id="converters" class="org.apache.karaf.shell.console.Converters">
@@ -83,9 +88,7 @@
     </service>
     <service auto-export="interfaces" ranking="-10">
         <bean class="org.apache.karaf.shell.console.help.SingleCommandHelpProvider">
-            <property name="io">
-                <reference interface="org.apache.felix.service.threadio.ThreadIO"/>
-            </property>
+            <property name="io" ref="threadIO"/>
         </bean>
     </service>
     <service auto-export="interfaces" ref="subShellHelpProvider" ranking="-10"/>

http://git-wip-us.apache.org/repos/asf/karaf/blob/94ffa5e5/shell/console/src/test/java/org/apache/felix/gogo/commands/Context.java
----------------------------------------------------------------------
diff --git a/shell/console/src/test/java/org/apache/felix/gogo/commands/Context.java b/shell/console/src/test/java/org/apache/felix/gogo/commands/Context.java
index 80f5309..4ef17e0 100644
--- a/shell/console/src/test/java/org/apache/felix/gogo/commands/Context.java
+++ b/shell/console/src/test/java/org/apache/felix/gogo/commands/Context.java
@@ -51,7 +51,7 @@ public class Context extends CommandProcessorImpl
 
     public void addCommand(String name, Object target)
     {
-        put("test:" + name, target);
+        addCommand("test", target, name);
     }
 
     public void set(String name, Object value)

http://git-wip-us.apache.org/repos/asf/karaf/blob/94ffa5e5/shell/console/src/test/java/org/apache/karaf/shell/console/jline/ConsoleTest.java
----------------------------------------------------------------------
diff --git a/shell/console/src/test/java/org/apache/karaf/shell/console/jline/ConsoleTest.java b/shell/console/src/test/java/org/apache/karaf/shell/console/jline/ConsoleTest.java
deleted file mode 100644
index 34546c3..0000000
--- a/shell/console/src/test/java/org/apache/karaf/shell/console/jline/ConsoleTest.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.karaf.shell.console.jline;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-
-import java.io.InputStream;
-import java.io.PrintStream;
-import java.security.PrivilegedAction;
-
-import javax.security.auth.Subject;
-
-import org.apache.felix.gogo.api.CommandSessionListener;
-import org.apache.felix.gogo.runtime.threadio.ThreadIOImpl;
-import org.apache.felix.service.command.CommandProcessor;
-import org.apache.felix.service.command.CommandSession;
-import org.apache.felix.service.command.Converter;
-import org.apache.karaf.jaas.boot.principal.RolePrincipal;
-import org.apache.karaf.shell.console.jline.Console.DelegateSession;
-import org.apache.karaf.shell.security.impl.SecuredCommandProcessorImpl;
-import org.easymock.EasyMock;
-import org.easymock.IAnswer;
-import org.junit.Test;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Filter;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.ServiceListener;
-import org.osgi.framework.ServiceReference;
-
-public class ConsoleTest {
-    @Test
-    public void testConsoleImpl() throws Exception {
-        ServiceReference<?> cmRef = EasyMock.createMock(ServiceReference.class);
-        EasyMock.expect(cmRef.getProperty(CommandProcessor.COMMAND_SCOPE)).andReturn("myscope").anyTimes();
-        EasyMock.expect(cmRef.getProperty(CommandProcessor.COMMAND_FUNCTION)).andReturn("myfunction").anyTimes();
-        EasyMock.replay(cmRef);
-        ServiceReference<?>[] cmRefs = new ServiceReference[] {cmRef};
-
-        BundleContext bc = EasyMock.createMock(BundleContext.class);
-        EasyMock.expect(bc.getServiceReference((Class<?>) EasyMock.anyObject())).andReturn(null).anyTimes();
-        EasyMock.expect(bc.getService((ServiceReference<?>) EasyMock.anyObject())).andReturn(null).anyTimes();
-        bc.addServiceListener(EasyMock.isA(ServiceListener.class), EasyMock.isA(String.class));
-        EasyMock.expectLastCall().anyTimes();
-        EasyMock.expect(bc.getServiceReferences((String) null,
-                "(&(osgi.command.scope=*)(osgi.command.function=*)" +
-                        "(|(org.apache.karaf.service.guard.roles=myrole)(!(org.apache.karaf.service.guard.roles=*))))")).andReturn(cmRefs).anyTimes();
-        EasyMock.expect(bc.getServiceReferences(Converter.class.getName(), null)).andReturn(null).anyTimes();
-        EasyMock.expect(bc.getServiceReferences(CommandSessionListener.class.getName(), null)).andReturn(null).anyTimes();
-        EasyMock.expect(bc.createFilter(EasyMock.isA(String.class))).andAnswer(new IAnswer<Filter>() {
-            @Override
-            public Filter answer() throws Throwable {
-                return FrameworkUtil.createFilter((String) EasyMock.getCurrentArguments()[0]);
-            }
-        }).anyTimes();
-        EasyMock.replay(bc);
-
-        final Console console = new Console(null, new ThreadIOImpl(), System.in, System.out, System.err, null, "UTF-8", null, bc);
-        assertTrue(console.session instanceof DelegateSession);
-
-        console.session.put("foo", "bar");
-
-        final DelegateSession ds = (DelegateSession) console.session;
-        assertNull("Precondition", ds.delegate);
-
-        Subject subject = new Subject();
-        subject.getPrincipals().add(new RolePrincipal("myrole"));
-
-        Subject.doAs(subject, new PrivilegedAction<Object>() {
-            @Override
-            public Object run() {
-                SecuredCommandProcessorImpl secCP = console.createSecuredCommandProcessor();
-                assertNotNull(ds.delegate);
-                assertEquals("Attributes set before the delegate was set should have been transferred",
-                        "bar", ds.get("foo"));
-                assertEquals("Attributes set before the delegate was set should have been transferred",
-                        "bar", ds.delegate.get("foo"));
-                assertSame(System.out, ds.delegate.getConsole());
-                assertSame(System.out, ds.getConsole());
-
-                assertTrue(secCP.getCommands().contains("myscope:myfunction"));
-
-                return null;
-            }
-        });
-    }
-
-    @Test
-    public void testDelegateSession() throws Exception {
-        DelegateSession ds = new DelegateSession();
-
-        ds.put("a", "b");
-        assertEquals("b", ds.get("a"));
-
-        TestSession ts = new TestSession();
-
-        assertNull("Precondition", ts.lastInvoked);
-
-        ds.setDelegate(ts);
-        assertEquals("put(a,b)", ts.lastInvoked);
-
-        ds.put("c", "d");
-        assertEquals("put(c,d)", ts.lastInvoked);
-
-        ds.execute("hello 1234");
-        assertEquals("execute(hello 1234)", ts.lastInvoked);
-
-        ds.close();
-        assertEquals("close", ts.lastInvoked);
-
-        ds.getKeyboard();
-        assertEquals("getKeyboard", ts.lastInvoked);
-
-        ds.getConsole();
-        assertEquals("getConsole", ts.lastInvoked);
-
-        ds.get("xyz");
-        assertEquals("get(xyz)", ts.lastInvoked);
-
-        ds.format("foo", 12);
-        assertEquals("format(foo,12)", ts.lastInvoked);
-
-        ds.convert(TestSession.class, "a string");
-        assertEquals("convert(TestSession,a string)", ts.lastInvoked);
-    }
-
-    static class TestSession implements CommandSession {
-        String lastInvoked;
-
-        @Override
-        public Object execute(CharSequence commandline) throws Exception {
-            lastInvoked = "execute(" + commandline + ")";
-            return null;
-        }
-
-        @Override
-        public void close() {
-            lastInvoked = "close";
-        }
-
-        @Override
-        public InputStream getKeyboard() {
-            lastInvoked = "getKeyboard";
-            return null;
-        }
-
-        @Override
-        public PrintStream getConsole() {
-            lastInvoked = "getConsole";
-            return null;
-        }
-
-        @Override
-        public Object get(String name) {
-            lastInvoked = "get(" + name + ")";
-            return null;
-        }
-
-        @Override
-        public void put(String name, Object value) {
-            lastInvoked = "put(" + name + "," + value + ")";
-        }
-
-        @Override
-        public CharSequence format(Object target, int level) {
-            lastInvoked = "format(" + target + "," + level + ")";
-            return null;
-        }
-
-        @Override
-        public Object convert(Class<?> type, Object instance) {
-            lastInvoked = "convert(" + type.getSimpleName() + "," + instance + ")";
-            return null;
-        }
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/karaf/blob/94ffa5e5/shell/console/src/test/java/org/apache/karaf/shell/security/impl/SecuredCommandProcessorImplTest.java
----------------------------------------------------------------------
diff --git a/shell/console/src/test/java/org/apache/karaf/shell/security/impl/SecuredCommandProcessorImplTest.java b/shell/console/src/test/java/org/apache/karaf/shell/security/impl/SecuredCommandProcessorImplTest.java
index 351a89f..0c66eb6 100644
--- a/shell/console/src/test/java/org/apache/karaf/shell/security/impl/SecuredCommandProcessorImplTest.java
+++ b/shell/console/src/test/java/org/apache/karaf/shell/security/impl/SecuredCommandProcessorImplTest.java
@@ -132,7 +132,7 @@ public class SecuredCommandProcessorImplTest {
     // Subclass to provide access to some protected members
     static class MySecuredCommandProcessorImpl extends SecuredCommandProcessorImpl {
         public MySecuredCommandProcessorImpl(BundleContext bc) {
-            super(bc);
+            super(bc, null);
         }
 
         Map<String, Object> getConstants() {

http://git-wip-us.apache.org/repos/asf/karaf/blob/94ffa5e5/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/ShellFactoryImpl.java
----------------------------------------------------------------------
diff --git a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/ShellFactoryImpl.java b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/ShellFactoryImpl.java
index 37fb37c..8668b3c 100644
--- a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/ShellFactoryImpl.java
+++ b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/ShellFactoryImpl.java
@@ -44,6 +44,8 @@ import org.apache.sshd.server.SessionAware;
 import org.apache.sshd.server.session.ServerSession;
 import org.osgi.framework.BundleContext;
 import org.osgi.service.blueprint.container.ReifiedType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * SSHD {@link org.apache.sshd.server.Command} factory which provides access to Shell.
@@ -56,6 +58,8 @@ public class ShellFactoryImpl implements Factory<Command> {
             JaasHelper.DelegatingProtectionDomain.class,
     };
 
+    private static final Logger LOGGER = LoggerFactory.getLogger(ShellFactoryImpl.class);
+
     private CommandProcessor commandProcessor;
     private ThreadIO threadIO;
     private BundleContext bundleContext;
@@ -111,63 +115,63 @@ public class ShellFactoryImpl implements Factory<Command> {
         }
 
         public void start(final Environment env) throws IOException {
-            try {
-                final Terminal terminal = new SshTerminal(env);
-                String encoding = env.getEnv().get("LC_CTYPE");
-                if (encoding != null && encoding.indexOf('.') > 0) {
-                    encoding = encoding.substring(encoding.indexOf('.') + 1);
-                }
-                Console console = new Console(commandProcessor,
-                                              threadIO,
-                                              in,
-                                              new PrintStream(new LfToCrLfFilterOutputStream(out), true),
-                                              new PrintStream(new LfToCrLfFilterOutputStream(err), true),
-                                              terminal,
-                                              encoding,
-                                              new Runnable() {
-                                                  public void run() {
-                                                      destroy();
-                                                  }
-                                              },
-                                              bundleContext);
-                final CommandSession session = console.getSession();
-                session.put("APPLICATION", System.getProperty("karaf.name", "root"));
-                for (Map.Entry<String,String> e : env.getEnv().entrySet()) {
-                    session.put(e.getKey(), e.getValue());
-                }
-                session.put("#LINES", new Function() {
-                    public Object execute(CommandSession session, List<Object> arguments) throws Exception {
-                        return Integer.toString(terminal.getHeight());
-                    }
-                });
-                session.put("#COLUMNS", new Function() {
-                    public Object execute(CommandSession session, List<Object> arguments) throws Exception {
-                        return Integer.toString(terminal.getWidth());
+            new Thread() {
+                @Override
+                public void run() {
+                    Subject subject = ShellImpl.this.session != null ? ShellImpl.this.session.getAttribute(KarafJaasAuthenticator.SUBJECT_ATTRIBUTE_KEY) : null;
+                    if (subject != null) {
+                        JaasHelper.doAs(subject, new PrivilegedAction<Object>() {
+                            public Object run() {
+                                runConsole();
+                                return null;
+                            }
+                        });
+                    } else {
+                        runConsole();
                     }
-                });
-                session.put(".jline.terminal", terminal);
-                new Thread(console) {
-                    @Override
-                    public void run() {
-                        Subject subject = ShellImpl.this.session != null ? ShellImpl.this.session.getAttribute(KarafJaasAuthenticator.SUBJECT_ATTRIBUTE_KEY) : null;
-                        if (subject != null) {
-                            JaasHelper.doAs(subject, new PrivilegedAction<Object>() {
-                                public Object run() {
-                                    doRun();
-                                    return null;
-                                }
-                            });
-                        } else {
-                            doRun();
+                }
+                protected void runConsole() {
+                    try {
+                        final Terminal terminal = new SshTerminal(env);
+                        String encoding = env.getEnv().get("LC_CTYPE");
+                        if (encoding != null && encoding.indexOf('.') > 0) {
+                            encoding = encoding.substring(encoding.indexOf('.') + 1);
                         }
+                        final Console console = new Console(commandProcessor,
+                                threadIO,
+                                in,
+                                new PrintStream(new LfToCrLfFilterOutputStream(out), true),
+                                new PrintStream(new LfToCrLfFilterOutputStream(err), true),
+                                terminal,
+                                encoding,
+                                new Runnable() {
+                                    public void run() {
+                                        ShellImpl.this.destroy();
+                                    }
+                                },
+                                bundleContext);
+                        final CommandSession session = console.getSession();
+                        session.put("APPLICATION", System.getProperty("karaf.name", "root"));
+                        for (Map.Entry<String,String> e : env.getEnv().entrySet()) {
+                            session.put(e.getKey(), e.getValue());
+                        }
+                        session.put("#LINES", new Function() {
+                            public Object execute(CommandSession session, List<Object> arguments) throws Exception {
+                                return Integer.toString(terminal.getHeight());
+                            }
+                        });
+                        session.put("#COLUMNS", new Function() {
+                            public Object execute(CommandSession session, List<Object> arguments) throws Exception {
+                                return Integer.toString(terminal.getWidth());
+                            }
+                        });
+                        session.put(".jline.terminal", terminal);
+                        console.run();
+                    } catch (Exception e) {
+                        LOGGER.warn("Unable to start shell", e);
                     }
-                    protected void doRun() {
-                        super.run();
-                    }
-                }.start();
-            } catch (Exception e) {
-                throw (IOException) new IOException("Unable to start shell").initCause(e);
-            }
+                }
+            }.start();
         }
 
         public void destroy() {


[2/2] git commit: [KARAF-3221] Incorporate the fix for FELIX-4637 into shell/core, fix the scr commands to use a service.ranking property

Posted by gn...@apache.org.
[KARAF-3221] Incorporate the fix for FELIX-4637 into shell/core, fix the scr commands to use a service.ranking property


Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/bf031161
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/bf031161
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/bf031161

Branch: refs/heads/karaf-2.x
Commit: bf0311611cd6a535bf384b062032442b64732924
Parents: 94ffa5e
Author: Guillaume Nodet <gn...@gmail.com>
Authored: Thu Sep 11 23:18:53 2014 +0200
Committer: Guillaume Nodet <gn...@gmail.com>
Committed: Thu Sep 11 23:19:10 2014 +0200

----------------------------------------------------------------------
 .../gogo/runtime/CommandProcessorImpl.java      | 358 +++++++++++++++++++
 .../impl/SecuredCommandProcessorImpl.java       |  20 +-
 .../shell/scr/ActivateCommandComponent.java     |   4 +-
 .../shell/scr/DeactivateCommandComponent.java   |   4 +-
 .../shell/scr/DetailsCommandComponent.java      |   4 +-
 .../karaf/shell/scr/ListCommandComponent.java   |   4 +-
 6 files changed, 387 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/bf031161/shell/console/src/main/java/org/apache/felix/gogo/runtime/CommandProcessorImpl.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/felix/gogo/runtime/CommandProcessorImpl.java b/shell/console/src/main/java/org/apache/felix/gogo/runtime/CommandProcessorImpl.java
new file mode 100644
index 0000000..72cff88
--- /dev/null
+++ b/shell/console/src/main/java/org/apache/felix/gogo/runtime/CommandProcessorImpl.java
@@ -0,0 +1,358 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.gogo.runtime;
+
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.apache.felix.gogo.api.CommandSessionListener;
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.felix.service.command.Converter;
+import org.apache.felix.service.command.Function;
+import org.apache.felix.service.threadio.ThreadIO;
+
+/**
+ * TODO: remove this class when upgraded to gogo-runtime >= 0.12.2, see FELIX-4637 / KARAF-3221
+ */
+public class CommandProcessorImpl implements CommandProcessor
+{
+    protected final Set<Converter> converters = new CopyOnWriteArraySet<Converter>();
+    protected final Set<CommandSessionListener> listeners = new CopyOnWriteArraySet<CommandSessionListener>();
+    protected final ConcurrentMap<String, Map<Object, Integer>> commands = new ConcurrentHashMap<String, Map<Object, Integer>>();
+    protected final Map<String, Object> constants = new ConcurrentHashMap<String, Object>();
+    protected final ThreadIO threadIO;
+    protected final WeakHashMap<CommandSession, Object> sessions = new WeakHashMap<CommandSession, Object>();
+    protected boolean stopped;
+
+    public CommandProcessorImpl(ThreadIO tio)
+    {
+        threadIO = tio;
+    }
+
+    public CommandSession createSession(InputStream in, PrintStream out, PrintStream err)
+    {
+        synchronized (sessions)
+        {
+            if (stopped) {
+                throw new IllegalStateException("CommandProcessor has been stopped");
+            }
+            CommandSessionImpl session = new CommandSessionImpl(this, in, out, err);
+            sessions.put(session, null);
+            return session;
+        }
+    }
+
+    public void stop()
+    {
+        synchronized (sessions)
+        {
+            stopped = true;
+            for (CommandSession session : sessions.keySet())
+            {
+                session.close();
+            }
+        }
+    }
+
+    public void addConverter(Converter c)
+    {
+        converters.add(c);
+    }
+
+    public void removeConverter(Converter c)
+    {
+        converters.remove(c);
+    }
+
+    public void addListener(CommandSessionListener l)
+    {
+        listeners.add(l);
+    }
+
+    public void removeListener(CommandSessionListener l)
+    {
+        listeners.remove(l);
+    }
+
+    public Set<String> getCommands()
+    {
+        return Collections.unmodifiableSet(commands.keySet());
+    }
+
+    Function getCommand(String name, final Object path)
+    {
+        int colon = name.indexOf(':');
+
+        if (colon < 0)
+        {
+            return null;
+        }
+
+        name = name.toLowerCase();
+        String cfunction = name.substring(colon);
+        boolean anyScope = (colon == 1 && name.charAt(0) == '*');
+
+        Map<Object, Integer> cmdMap = commands.get(name);
+
+        if (null == cmdMap && anyScope)
+        {
+            String scopePath = (null == path ? "*" : path.toString());
+
+            for (String scope : scopePath.split(":"))
+            {
+                if (scope.equals("*"))
+                {
+                    for (Entry<String, Map<Object, Integer>> entry : commands.entrySet())
+                    {
+                        if (entry.getKey().endsWith(cfunction))
+                        {
+                            cmdMap = entry.getValue();
+                            break;
+                        }
+                    }
+                }
+                else
+                {
+                    cmdMap = commands.get(scope + cfunction);
+                    if (cmdMap != null)
+                    {
+                        break;
+                    }
+                }
+            }
+        }
+
+        Object cmd = null;
+        if (cmdMap != null && !cmdMap.isEmpty())
+        {
+            for (Entry<Object, Integer> e : cmdMap.entrySet())
+            {
+                if (cmd == null || e.getValue() > cmdMap.get(cmd))
+                {
+                    cmd = e.getKey();
+                }
+            }
+        }
+        if ((null == cmd) || (cmd instanceof Function))
+        {
+            return (Function) cmd;
+        }
+
+        return new CommandProxy(cmd, cfunction.substring(1));
+    }
+
+    public void addCommand(String scope, Object target)
+    {
+        Class<?> tc = (target instanceof Class<?>) ? (Class<?>) target
+            : target.getClass();
+        addCommand(scope, target, tc);
+    }
+
+    public void addCommand(String scope, Object target, Class<?> functions)
+    {
+        addCommand(scope, target, functions, 0);
+    }
+
+    public void addCommand(String scope, Object target, Class<?> functions, int ranking)
+    {
+        if (target == null)
+        {
+            return;
+        }
+
+        String[] names = getFunctions(functions);
+        for (String function : names)
+        {
+            addCommand(scope, target, function, ranking);
+        }
+    }
+
+    public Object addConstant(String name, Object target)
+    {
+        return constants.put(name, target);
+    }
+
+    public Object removeConstant(String name)
+    {
+        return constants.remove(name);
+    }
+
+    public void addCommand(String scope, Object target, String function)
+    {
+        addCommand(scope, target, function, 0);
+    }
+
+    public void addCommand(String scope, Object target, String function, int ranking)
+    {
+        String key = (scope + ":" + function).toLowerCase();
+        Map<Object, Integer> cmdMap = commands.get(key);
+        if (cmdMap == null)
+        {
+            commands.putIfAbsent(key, new LinkedHashMap<Object, Integer>());
+            cmdMap = commands.get(key);
+        }
+        cmdMap.put(target, ranking);
+    }
+
+    public void removeCommand(String scope, String function)
+    {
+        // TODO: WARNING: this method does remove all mapping for scope:function
+        String key = (scope + ":" + function).toLowerCase();
+        commands.remove(key);
+    }
+
+    public void removeCommand(String scope, String function, Object target)
+    {
+        // TODO: WARNING: this method does remove all mapping for scope:function
+        String key = (scope + ":" + function).toLowerCase();
+        Map<Object, Integer> cmdMap = commands.get(key);
+        if (cmdMap != null)
+        {
+            cmdMap.remove(target);
+        }
+    }
+
+    public void removeCommand(Object target)
+    {
+        for (Map<Object, Integer> cmdMap : commands.values())
+        {
+            cmdMap.remove(target);
+        }
+    }
+
+    private String[] getFunctions(Class<?> target)
+    {
+        String[] functions;
+        Set<String> list = new TreeSet<String>();
+        Method methods[] = target.getMethods();
+        for (Method m : methods)
+        {
+            if (m.getDeclaringClass().equals(Object.class))
+            {
+                continue;
+            }
+            list.add(m.getName());
+            if (m.getName().startsWith("get"))
+            {
+                String s = m.getName().substring(3);
+                if (s.length() > 0)
+                {
+                    list.add(s.substring(0, 1).toLowerCase() + s.substring(1));
+                }
+            }
+        }
+
+        functions = list.toArray(new String[list.size()]);
+        return functions;
+    }
+
+    public Object convert(Class<?> desiredType, Object in)
+    {
+        for (Converter c : converters)
+        {
+            try
+            {
+                Object converted = c.convert(desiredType, in);
+                if (converted != null)
+                {
+                    return converted;
+                }
+            }
+            catch (Exception e)
+            {
+                e.printStackTrace();
+            }
+        }
+        return null;
+    }
+
+    // eval is needed to force expansions to be treated as commands (FELIX-1473)
+    public Object eval(CommandSession session, Object[] argv) throws Exception
+    {
+        StringBuilder buf = new StringBuilder();
+
+        for (Object arg : argv)
+        {
+            if (buf.length() > 0)
+                buf.append(' ');
+            buf.append(arg);
+        }
+
+        return session.execute(buf);
+    }
+
+    void beforeExecute(CommandSession session, CharSequence commandline)
+    {
+        for (CommandSessionListener l : listeners)
+        {
+            try
+            {
+                l.beforeExecute(session, commandline);
+            }
+            catch (Throwable t)
+            {
+                // Ignore
+            }
+        }
+    }
+
+    void afterExecute(CommandSession session, CharSequence commandline,
+        Exception exception)
+    {
+        for (CommandSessionListener l : listeners)
+        {
+            try
+            {
+                l.afterExecute(session, commandline, exception);
+            }
+            catch (Throwable t)
+            {
+                // Ignore
+            }
+        }
+    }
+
+    void afterExecute(CommandSession session, CharSequence commandline, Object result)
+    {
+        for (CommandSessionListener l : listeners)
+        {
+            try
+            {
+                l.afterExecute(session, commandline, result);
+            }
+            catch (Throwable t)
+            {
+                // Ignore
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/bf031161/shell/console/src/main/java/org/apache/karaf/shell/security/impl/SecuredCommandProcessorImpl.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/security/impl/SecuredCommandProcessorImpl.java b/shell/console/src/main/java/org/apache/karaf/shell/security/impl/SecuredCommandProcessorImpl.java
index 3a83958..0e16a56 100644
--- a/shell/console/src/main/java/org/apache/karaf/shell/security/impl/SecuredCommandProcessorImpl.java
+++ b/shell/console/src/main/java/org/apache/karaf/shell/security/impl/SecuredCommandProcessorImpl.java
@@ -25,6 +25,7 @@ import org.apache.felix.service.command.Converter;
 import org.apache.felix.service.threadio.ThreadIO;
 import org.apache.karaf.jaas.boot.principal.RolePrincipal;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
 import org.osgi.framework.Filter;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
@@ -114,8 +115,21 @@ public class SecuredCommandProcessorImpl extends CommandProcessorImpl {
             {
                 Object scope = reference.getProperty(CommandProcessor.COMMAND_SCOPE);
                 Object function = reference.getProperty(CommandProcessor.COMMAND_FUNCTION);
+                Object ranking = reference.getProperty(Constants.SERVICE_RANKING);
                 List<Object> commands = new ArrayList<Object>();
 
+                int rank = 0;
+                if (ranking != null)
+                {
+                    try
+                    {
+                        rank = Integer.parseInt(ranking.toString());
+                    }
+                    catch (NumberFormatException e)
+                    {
+                        // Ignore
+                    }
+                }
                 if (scope != null && function != null)
                 {
                     Map<String, CommandProxy> proxyMap = new HashMap<String, CommandProxy>();
@@ -125,7 +139,7 @@ public class SecuredCommandProcessorImpl extends CommandProcessorImpl {
                         {
                             CommandProxy target = new CommandProxy(context, reference, f.toString());
                             proxyMap.put(f.toString(), target);
-                            addCommand(scope.toString(), target, f.toString());
+                            addCommand(scope.toString(), target, f.toString(), rank);
                             commands.add(target);
                         }
                     }
@@ -133,7 +147,7 @@ public class SecuredCommandProcessorImpl extends CommandProcessorImpl {
                     {
                         CommandProxy target = new CommandProxy(context, reference, function.toString());
                         proxyMap.put(function.toString(), target);
-                        addCommand(scope.toString(), target, function.toString());
+                        addCommand(scope.toString(), target, function.toString(), rank);
                         commands.add(target);
                     }
                     proxies.put(reference, proxyMap);
@@ -153,7 +167,7 @@ public class SecuredCommandProcessorImpl extends CommandProcessorImpl {
                     Map<String, CommandProxy> proxyMap = proxies.remove(reference);
                     for (Map.Entry<String, CommandProxy> entry : proxyMap.entrySet())
                     {
-                        removeCommand(scope.toString(), entry.getKey());
+                        removeCommand(scope.toString(), entry.getKey(), entry.getValue());
                     }
                 }
 

http://git-wip-us.apache.org/repos/asf/karaf/blob/bf031161/shell/scr/src/main/java/org/apache/karaf/shell/scr/ActivateCommandComponent.java
----------------------------------------------------------------------
diff --git a/shell/scr/src/main/java/org/apache/karaf/shell/scr/ActivateCommandComponent.java b/shell/scr/src/main/java/org/apache/karaf/shell/scr/ActivateCommandComponent.java
index 0122ab5..b553b82 100644
--- a/shell/scr/src/main/java/org/apache/karaf/shell/scr/ActivateCommandComponent.java
+++ b/shell/scr/src/main/java/org/apache/karaf/shell/scr/ActivateCommandComponent.java
@@ -30,6 +30,7 @@ import org.apache.karaf.shell.console.CompletableFunction;
 import org.apache.karaf.shell.console.Completer;
 import org.apache.karaf.shell.scr.action.ActivateAction;
 import org.apache.karaf.shell.scr.completer.ActivateCompleter;
+import org.osgi.framework.Constants;
 
 /**
  * Shell Command used to activate a Declarative Service Component.
@@ -42,7 +43,8 @@ import org.apache.karaf.shell.scr.completer.ActivateCompleter;
         properties={ 
             ScrCommandConstants.OSGI_COMMAND_SCOPE_KEY+"="+ScrCommandConstants.SCR_COMMAND, 
             ScrCommandConstants.OSGI_COMMAND_FUNCTION_KEY+"="+ScrCommandConstants.ACTIVATE_FUNCTION,
-            ScrCommandConstants.HIDDEN_COMPONENT_KEY + "=true"})
+            ScrCommandConstants.HIDDEN_COMPONENT_KEY + "=true",
+            Constants.SERVICE_RANKING + "=1"})
 public class ActivateCommandComponent extends ScrCommandSupport {
 
     public static final String COMPONENT_NAME = "ActivateCommand";

http://git-wip-us.apache.org/repos/asf/karaf/blob/bf031161/shell/scr/src/main/java/org/apache/karaf/shell/scr/DeactivateCommandComponent.java
----------------------------------------------------------------------
diff --git a/shell/scr/src/main/java/org/apache/karaf/shell/scr/DeactivateCommandComponent.java b/shell/scr/src/main/java/org/apache/karaf/shell/scr/DeactivateCommandComponent.java
index e86631d..dfe2629 100644
--- a/shell/scr/src/main/java/org/apache/karaf/shell/scr/DeactivateCommandComponent.java
+++ b/shell/scr/src/main/java/org/apache/karaf/shell/scr/DeactivateCommandComponent.java
@@ -30,6 +30,7 @@ import org.apache.karaf.shell.console.CompletableFunction;
 import org.apache.karaf.shell.console.Completer;
 import org.apache.karaf.shell.scr.action.DeactivateAction;
 import org.apache.karaf.shell.scr.completer.DeactivateCompleter;
+import org.osgi.framework.Constants;
 
 /**
  * Shell Command used to deactivate a Declarative Service Component.
@@ -42,7 +43,8 @@ import org.apache.karaf.shell.scr.completer.DeactivateCompleter;
         properties={ 
             ScrCommandConstants.OSGI_COMMAND_SCOPE_KEY+"="+ScrCommandConstants.SCR_COMMAND, 
             ScrCommandConstants.OSGI_COMMAND_FUNCTION_KEY+"="+ScrCommandConstants.DEACTIVATE_FUNCTION,
-            ScrCommandConstants.HIDDEN_COMPONENT_KEY + "=true"})
+            ScrCommandConstants.HIDDEN_COMPONENT_KEY + "=true",
+            Constants.SERVICE_RANKING + "=1"})
 public class DeactivateCommandComponent extends ScrCommandSupport {
 
     public static final String COMPONENT_NAME = "DeactivateCommand";

http://git-wip-us.apache.org/repos/asf/karaf/blob/bf031161/shell/scr/src/main/java/org/apache/karaf/shell/scr/DetailsCommandComponent.java
----------------------------------------------------------------------
diff --git a/shell/scr/src/main/java/org/apache/karaf/shell/scr/DetailsCommandComponent.java b/shell/scr/src/main/java/org/apache/karaf/shell/scr/DetailsCommandComponent.java
index 83e3f71..fd5c1a8 100644
--- a/shell/scr/src/main/java/org/apache/karaf/shell/scr/DetailsCommandComponent.java
+++ b/shell/scr/src/main/java/org/apache/karaf/shell/scr/DetailsCommandComponent.java
@@ -30,6 +30,7 @@ import org.apache.karaf.shell.console.CompletableFunction;
 import org.apache.karaf.shell.console.Completer;
 import org.apache.karaf.shell.scr.action.DetailsAction;
 import org.apache.karaf.shell.scr.completer.DetailsCompleter;
+import org.osgi.framework.Constants;
 
 /**
  * Shell Command that prints the current state details of a given
@@ -43,7 +44,8 @@ import org.apache.karaf.shell.scr.completer.DetailsCompleter;
         properties={ 
             ScrCommandConstants.OSGI_COMMAND_SCOPE_KEY+"="+ScrCommandConstants.SCR_COMMAND, 
             ScrCommandConstants.OSGI_COMMAND_FUNCTION_KEY+"="+ScrCommandConstants.DETAILS_FUNCTION,
-            ScrCommandConstants.HIDDEN_COMPONENT_KEY + "=true"})
+            ScrCommandConstants.HIDDEN_COMPONENT_KEY + "=true",
+            Constants.SERVICE_RANKING + "=1"})
 public class DetailsCommandComponent extends ScrCommandSupport {
 
     public static final String COMPONENT_NAME = "DetailsCommand";

http://git-wip-us.apache.org/repos/asf/karaf/blob/bf031161/shell/scr/src/main/java/org/apache/karaf/shell/scr/ListCommandComponent.java
----------------------------------------------------------------------
diff --git a/shell/scr/src/main/java/org/apache/karaf/shell/scr/ListCommandComponent.java b/shell/scr/src/main/java/org/apache/karaf/shell/scr/ListCommandComponent.java
index 44e86b2..6d19363 100644
--- a/shell/scr/src/main/java/org/apache/karaf/shell/scr/ListCommandComponent.java
+++ b/shell/scr/src/main/java/org/apache/karaf/shell/scr/ListCommandComponent.java
@@ -28,6 +28,7 @@ import org.apache.felix.scr.ScrService;
 import org.apache.karaf.shell.console.CompletableFunction;
 import org.apache.karaf.shell.console.Completer;
 import org.apache.karaf.shell.scr.action.ListAction;
+import org.osgi.framework.Constants;
 
 /**
  * Shell Command that lists the available Declarative Service Components.
@@ -40,7 +41,8 @@ import org.apache.karaf.shell.scr.action.ListAction;
         properties={ 
             ScrCommandConstants.OSGI_COMMAND_SCOPE_KEY+"="+ScrCommandConstants.SCR_COMMAND, 
             ScrCommandConstants.OSGI_COMMAND_FUNCTION_KEY+"="+ScrCommandConstants.LIST_FUNCTION,
-            ScrCommandConstants.HIDDEN_COMPONENT_KEY + "=true"})
+            ScrCommandConstants.HIDDEN_COMPONENT_KEY + "=true",
+            Constants.SERVICE_RANKING + "=1"})
 public class ListCommandComponent extends ScrCommandSupport {
 
     public static final String COMPONENT_NAME = "ListCommand";