Repository: karaf
Updated Branches:
  refs/heads/master e0eda8a36 -> 8a4c9124a


KARAF-2762 Refactored CommandExporter and implemented removal on stop


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

Branch: refs/heads/master
Commit: 8a4c9124a5336208f05a4b239bea48c917ed1baa
Parents: e0eda8a
Author: Christian Schneider <[email protected]>
Authored: Mon Feb 17 13:54:19 2014 +0100
Committer: Christian Schneider <[email protected]>
Committed: Mon Feb 17 13:54:19 2014 +0100

----------------------------------------------------------------------
 .../karaf/shell/exporter/ActionCommand.java     | 132 +++++++++++++++++++
 .../karaf/shell/exporter/ActionTracker.java     |  75 +++++++++++
 .../apache/karaf/shell/exporter/Activator.java  |  32 +----
 .../karaf/shell/exporter/CommandExporter.java   | 131 ------------------
 4 files changed, 210 insertions(+), 160 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/8a4c9124/shell/command-exporter/src/main/java/org/apache/karaf/shell/exporter/ActionCommand.java
----------------------------------------------------------------------
diff --git 
a/shell/command-exporter/src/main/java/org/apache/karaf/shell/exporter/ActionCommand.java
 
b/shell/command-exporter/src/main/java/org/apache/karaf/shell/exporter/ActionCommand.java
new file mode 100644
index 0000000..946d120
--- /dev/null
+++ 
b/shell/command-exporter/src/main/java/org/apache/karaf/shell/exporter/ActionCommand.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.shell.exporter;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.gogo.commands.Action;
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.CommandWithAction;
+import org.apache.felix.gogo.commands.basic.AbstractCommand;
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.Function;
+import org.apache.karaf.shell.console.CompletableFunction;
+import org.apache.karaf.shell.console.Completer;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Wraps an Action as a command using a template for the action injects
+ */
+@SuppressWarnings("deprecation")
+public class ActionCommand extends AbstractCommand implements 
CompletableFunction {
+    private static Logger logger = 
LoggerFactory.getLogger(ActionCommand.class);
+
+    private Action actionTemplate;
+    private List<Completer> completers = new ArrayList<Completer>();
+
+    public ActionCommand(Action actionTemplate) {
+        this.actionTemplate = actionTemplate;
+        addCompleters();
+    }
+    
+    public ServiceRegistration<?> registerService(BundleContext context) {
+        Class<? extends Action> actionClass = actionTemplate.getClass();
+        Command cmd = actionClass.getAnnotation(Command.class);
+        if (cmd == null) {
+            throw new IllegalArgumentException("Action class " + actionClass
+                                               + " is not annotated with 
@Command");
+        }
+        String[] interfaces = new String[] {
+            Function.class.getName(), 
+            CommandWithAction.class.getName(),
+            AbstractCommand.class.getName()
+        };
+        Hashtable<String, String> props = new Hashtable<String, String>();
+        props.put(CommandProcessor.COMMAND_SCOPE, cmd.scope());
+        props.put(CommandProcessor.COMMAND_FUNCTION, cmd.name());
+        logger.info("Registering command " + cmd.scope() + ":" + cmd.name() + 
" in the name of bundle " + context.getBundle().getBundleId());
+        return context.registerService(interfaces, this, props);
+    }
+
+    @Override
+    public Class<? extends Action> getActionClass() {
+        return actionTemplate.getClass();
+    }
+
+    @Override
+    public Action createNewAction() {
+        try {
+            Action newAction = actionTemplate.getClass().newInstance();
+            copyFields(newAction);
+            return newAction;
+        } catch (InstantiationException e) {
+            throw new RuntimeException(e);
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public List<Completer> getCompleters() {
+        return completers;
+    }
+
+    @Override
+    public Map<String, Completer> getOptionalCompleters() {
+        return null;
+    }
+
+    private void copyFields(Action newAction) {
+        Field[] fields = actionTemplate.getClass().getDeclaredFields();
+        for (Field field : fields) {
+            try {
+                if (!field.isAccessible()) {
+                    field.setAccessible(true);
+                }
+                Object value = field.get(actionTemplate);
+                field.set(newAction, value);
+            } catch (Exception e) {
+                throw new RuntimeException(e.getMessage(), e);
+            }
+        }
+    }
+
+    private void addCompleters() {
+        for (Field field : actionTemplate.getClass().getDeclaredFields()) {
+            if (Completer.class.isAssignableFrom(field.getType())) {
+                try {
+                    if (!field.isAccessible()) {
+                        field.setAccessible(true);
+                    }
+                    this.completers.add((Completer)field.get(actionTemplate));
+                } catch (Exception e) {
+                    logger.warn("Error setting completer from field " + 
field.getName());
+                }
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/8a4c9124/shell/command-exporter/src/main/java/org/apache/karaf/shell/exporter/ActionTracker.java
----------------------------------------------------------------------
diff --git 
a/shell/command-exporter/src/main/java/org/apache/karaf/shell/exporter/ActionTracker.java
 
b/shell/command-exporter/src/main/java/org/apache/karaf/shell/exporter/ActionTracker.java
new file mode 100644
index 0000000..55cd06e
--- /dev/null
+++ 
b/shell/command-exporter/src/main/java/org/apache/karaf/shell/exporter/ActionTracker.java
@@ -0,0 +1,75 @@
+package org.apache.karaf.shell.exporter;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.felix.gogo.commands.Action;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Tracks services that implement {@link 
org.apache.felix.gogo.commands.Action}, 
+ * wraps each into an ActionCommand 
+ * and exports the command as a service in the name of the bundle exporting 
the Action
+ */
+@SuppressWarnings("deprecation")
+final class ActionTracker extends ServiceTracker<Action, Action> {
+    private Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    @SuppressWarnings("rawtypes")
+    private Map<ServiceReference, ServiceRegistration> registrations = 
+        new ConcurrentHashMap<ServiceReference, ServiceRegistration>();
+
+    ActionTracker(BundleContext context, Class<Action> clazz,
+                         ServiceTrackerCustomizer<Action, Action> customizer) {
+        super(context, clazz, customizer);
+    }
+
+    @Override
+    public Action addingService(ServiceReference<Action> reference) {
+        if (!registrations.containsKey(reference)) {
+            Bundle userBundle = reference.getBundle();
+            try {
+                BundleContext context = userBundle.getBundleContext();
+                ActionCommand command = new 
ActionCommand(context.getService(reference));
+                registrations.put(reference, command.registerService(context));
+            } catch (Exception e) {
+                logger.warn("Error exporting action as command from service of 
bundle " 
+                    + userBundle.getSymbolicName()
+                    + "[" + userBundle.getBundleId() + "]", e);
+            }
+        }
+        return super.addingService(reference);
+    }
+
+    @Override
+    public void removedService(ServiceReference<Action> reference, Action 
service) {
+        if (registrations.containsKey(reference)) {
+            try {
+                registrations.remove(reference).unregister();
+            } catch (Exception e) {
+                // Ignore .. might already be unregistered if exporting bundle 
stopped
+            }
+        }
+        super.removedService(reference, service);
+    }
+
+    @Override
+    public void close() {
+        for (ServiceRegistration<?> reg : registrations.values()) {
+            try {
+                reg.unregister();
+            } catch (Exception e) {
+                // Ignore .. might already be unregistered if exporting bundle 
stopped
+            }
+        }
+        registrations.clear();
+        super.close();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/karaf/blob/8a4c9124/shell/command-exporter/src/main/java/org/apache/karaf/shell/exporter/Activator.java
----------------------------------------------------------------------
diff --git 
a/shell/command-exporter/src/main/java/org/apache/karaf/shell/exporter/Activator.java
 
b/shell/command-exporter/src/main/java/org/apache/karaf/shell/exporter/Activator.java
index 8ce28f4..34eb761 100644
--- 
a/shell/command-exporter/src/main/java/org/apache/karaf/shell/exporter/Activator.java
+++ 
b/shell/command-exporter/src/main/java/org/apache/karaf/shell/exporter/Activator.java
@@ -14,49 +14,23 @@
 package org.apache.karaf.shell.exporter;
 
 import org.apache.felix.gogo.commands.Action;
-import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
 import org.osgi.util.tracker.ServiceTracker;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 @SuppressWarnings("deprecation")
 public class Activator implements BundleActivator {
-    Logger logger = LoggerFactory.getLogger(this.getClass());
+    private ServiceTracker<Action, Action> tracker;
     
     @Override
     public void start(BundleContext context) throws Exception {
-        ServiceTracker<Action, Action> tracker = new ServiceTracker<Action, 
Action>(context, Action.class, null) {
-
-            @Override
-            public Action addingService(ServiceReference<Action> reference) {
-                Bundle userBundle = reference.getBundle();
-                try {
-                    Action action = context.getService(reference);
-                    CommandExporter.export(userBundle.getBundleContext(), 
action);
-                    logger.info("Service registered");
-                } catch (Exception e) {
-                    logger.warn("Error exporting action from service of bundle 
" 
-                        + userBundle.getSymbolicName()
-                        + "[" + userBundle.getBundleId() + "]", e);
-                }
-                return super.addingService(reference);
-            }
-
-            @Override
-            public void removedService(ServiceReference<Action> reference, 
Action service) {
-                // TODO implement removing of commands
-                super.removedService(reference, service);
-            }
-            
-        };
+        tracker = new ActionTracker(context, Action.class, null);
         tracker.open();
     }
 
     @Override
     public void stop(BundleContext context) throws Exception {
+        tracker.close();
     }
 
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/8a4c9124/shell/command-exporter/src/main/java/org/apache/karaf/shell/exporter/CommandExporter.java
----------------------------------------------------------------------
diff --git 
a/shell/command-exporter/src/main/java/org/apache/karaf/shell/exporter/CommandExporter.java
 
b/shell/command-exporter/src/main/java/org/apache/karaf/shell/exporter/CommandExporter.java
deleted file mode 100644
index a9c75c3..0000000
--- 
a/shell/command-exporter/src/main/java/org/apache/karaf/shell/exporter/CommandExporter.java
+++ /dev/null
@@ -1,131 +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.exporter;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.felix.gogo.commands.Action;
-import org.apache.felix.gogo.commands.Command;
-import org.apache.felix.gogo.commands.CommandWithAction;
-import org.apache.felix.gogo.commands.basic.AbstractCommand;
-import org.apache.felix.service.command.CommandProcessor;
-import org.apache.felix.service.command.Function;
-import org.apache.karaf.shell.console.CompletableFunction;
-import org.apache.karaf.shell.console.Completer;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceRegistration;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Exports an Action as a command using a template for the action injects
- */
-@SuppressWarnings("deprecation")
-public class CommandExporter extends AbstractCommand implements 
CompletableFunction {
-    private static Logger logger = 
LoggerFactory.getLogger(CommandExporter.class);
-
-    private Action actionTemplate;
-    private List<Completer> completers = new ArrayList<Completer>();
-
-    public CommandExporter(Action actionTemplate)
-    {
-        this.actionTemplate = actionTemplate;
-        addCompleters();
-    }
-
-    public Class<? extends Action> getActionClass()
-    {
-        return actionTemplate.getClass();
-    }
-
-    public Action createNewAction() {
-        try {
-            Action newAction = actionTemplate.getClass().newInstance();
-            copyFields(newAction);
-            return newAction;
-        } catch (InstantiationException e) {
-            throw new RuntimeException(e);
-        } catch (IllegalAccessException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    private void copyFields(Action newAction) {
-        Field[] fields = actionTemplate.getClass().getDeclaredFields();
-        for (Field field : fields) {
-            try {
-                field.setAccessible(true);
-                Object value = field.get(actionTemplate);
-                field.set(newAction, value);
-            } catch (Exception e) {
-                throw new RuntimeException(e.getMessage(), e);
-            }
-        }
-    }
-
-    @SuppressWarnings("rawtypes")
-    public static ServiceRegistration export(BundleContext context, Action 
actionTemplate)
-    {
-        Class<? extends Action> actionClass = actionTemplate.getClass();
-        logger.info(actionClass.getName());
-        Command cmd = actionClass.getAnnotation(Command.class);
-        if (cmd == null)
-        {
-            throw new IllegalArgumentException("Action class is not annotated 
with @Command");
-        }
-        Hashtable<String, String> props = new Hashtable<String, String>();
-        props.put(CommandProcessor.COMMAND_SCOPE, cmd.scope());
-        props.put(CommandProcessor.COMMAND_FUNCTION, cmd.name());
-        CommandExporter command = new CommandExporter(actionTemplate);
-        return context.registerService(new String[]{
-                                                    Function.class.getName(),
-                                                    
CommandWithAction.class.getName(),
-                                                    
AbstractCommand.class.getName()
-                                                    }, 
-                                       command, props);
-    }
-
-    private void addCompleters() {
-        for (Field field : actionTemplate.getClass().getDeclaredFields()) {
-            field.setAccessible(true);
-            if (Completer.class.isAssignableFrom(field.getType())) {
-                try {
-                    this.completers.add((Completer)field.get(actionTemplate));
-                } catch (Exception e) {
-                    logger.warn("Error setting completer from field " + 
field.getName());
-                }
-            }
-        }
-    }
-
-    @Override
-    public List<Completer> getCompleters() {
-        return completers;
-    }
-
-    @Override
-    public Map<String, Completer> getOptionalCompleters() {
-        return null;
-    }
-
-}

Reply via email to