changeset 395:053bc97253ad

Nested dynamic menus Reviewed-by: neugens, vanaltj Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-June/001845.html
author Omair Majid <omajid@redhat.com>
date Tue, 19 Jun 2012 15:37:59 -0400
parents 963d557a7d00
children 776aa41e0dba
files client/core/src/main/java/com/redhat/thermostat/client/internal/MainView.java client/core/src/main/java/com/redhat/thermostat/client/internal/MainWindowControllerImpl.java client/core/src/main/java/com/redhat/thermostat/client/internal/MenuRegistry.java client/core/src/main/java/com/redhat/thermostat/client/internal/ThermostatExtensionRegistry.java client/core/src/main/java/com/redhat/thermostat/client/internal/VMTreeDecoratorRegistry.java client/core/src/main/java/com/redhat/thermostat/client/internal/VMTreeFilterRegistry.java client/core/src/main/java/com/redhat/thermostat/client/osgi/service/MenuAction.java client/core/src/main/java/com/redhat/thermostat/client/ui/MainWindow.java client/core/src/main/java/com/redhat/thermostat/client/ui/MenuHelper.java client/core/src/test/java/com/redhat/thermostat/client/internal/MainWindowControllerImplTest.java client/core/src/test/java/com/redhat/thermostat/client/internal/MenuRegistryTest.java client/core/src/test/java/com/redhat/thermostat/client/ui/MainWindowTest.java client/core/src/test/java/com/redhat/thermostat/client/ui/MenuHelperTest.java client/living-vm-filter/src/main/java/com/redhat/thermostat/client/filter/vm/LivingVMFilterMenuAction.java client/living-vm-filter/src/main/java/com/redhat/thermostat/client/filter/vm/VMFilterActivator.java
diffstat 15 files changed, 648 insertions(+), 259 deletions(-) [+]
line wrap: on
line diff
--- a/client/core/src/main/java/com/redhat/thermostat/client/internal/MainView.java	Tue Jun 19 20:58:05 2012 +0200
+++ b/client/core/src/main/java/com/redhat/thermostat/client/internal/MainView.java	Tue Jun 19 15:37:59 2012 -0400
@@ -78,9 +78,17 @@
 
     void setSubView(Component view);
 
-    void addMenu(String parentMenuName, MenuAction action);
+    /**
+     * Adds a menu item to the window. Assumes the menu path is valid (has a
+     * non-zero length) and doesn't collide with existing menus.
+     */
+    void addMenu(MenuAction action);
 
-    void removeMenu(String parentMenuName, MenuAction action);
+    /**
+     * Removes a menu item to the window. Assumes the menu path is valid (has a
+     * non-zero length) and doesn't collide with existing menus.
+     */
+    void removeMenu(MenuAction action);
 
     void showVMContextActions(List<VMContextAction> actions, MouseEvent e);
 }
--- a/client/core/src/main/java/com/redhat/thermostat/client/internal/MainWindowControllerImpl.java	Tue Jun 19 20:58:05 2012 +0200
+++ b/client/core/src/main/java/com/redhat/thermostat/client/internal/MainWindowControllerImpl.java	Tue Jun 19 15:37:59 2012 -0400
@@ -97,22 +97,35 @@
     private ApplicationInfo appInfo;
 
     private UiFacadeFactory facadeFactory;
+
+    // FIXME: sort out the code duplication in the registry listeners
+
     private MenuRegistry menuRegistry;
-    private MenuRegistry.MenuListener menuListener = new MenuRegistry.MenuListener() {
-
+    private ActionListener<ThermostatExtensionRegistry.Action> menuListener =
+            new ActionListener<ThermostatExtensionRegistry.Action>()
+    {
         @Override
-        public void removed(String parentMenuName, MenuAction action) {
-            view.removeMenu(parentMenuName, action);
-        }
+        public void actionPerformed(
+            ActionEvent<ThermostatExtensionRegistry.Action> actionEvent) {
+            MenuAction action = (MenuAction) actionEvent.getPayload();
+
+            switch (actionEvent.getActionId()) {
+            case SERVICE_ADDED:
+                view.addMenu(action);
+                break;
 
-        @Override
-        public void added(String parentMenuName, MenuAction action) {
-            view.addMenu(parentMenuName, action);
+            case SERVICE_REMOVED:
+                view.removeMenu(action);
+                break;
+
+            default:
+                logger.log(Level.WARNING, "received unknown event from MenuRegistry: " +
+                                           actionEvent.getActionId());
+                break;
+            }
         }
     };
 
-    // FIXME: sort out the code duplication in the registry listeners
-    
     private TreeViewFilter treeFilter;
     private VMTreeFilterRegistry filterRegistry;
     private ActionListener<ThermostatExtensionRegistry.Action> filterListener =
@@ -220,7 +233,7 @@
 
         updateView();
 
-        menuRegistry.addMenuListener(menuListener);
+        menuRegistry.addActionListener(menuListener);
         menuRegistry.start();
         
         filterRegistry.addActionListener(filterListener);
@@ -252,23 +265,23 @@
     }
 
     /**
-     * This method is for testing purpouse only
+     * This method is for testing purposes only
      */
     Filter getTreeFilter() {
         return treeFilter;
     }
     
     /**
-     * This method is for testing purpouse only
+     * This method is for testing purposes only
      */ 
     List<ReferenceDecorator> getVmTreeDecorators() {
         return vmTreeDecorators;
     }
     
     /**
-     * This method is for testing purpouse only
+     * This method is for testing purposes only
      */
-    MenuRegistry.MenuListener getMenuListener() {
+    ActionListener<ThermostatExtensionRegistry.Action> getMenuListener() {
         return menuListener;
     }
     
@@ -373,7 +386,7 @@
     }
 
     private void shutdownApplication() {
-        menuRegistry.removeMenuListener(menuListener);
+        menuRegistry.removeActionListener(menuListener);
         menuListener = null;
         menuRegistry.stop();
 
--- a/client/core/src/main/java/com/redhat/thermostat/client/internal/MenuRegistry.java	Tue Jun 19 20:58:05 2012 +0200
+++ b/client/core/src/main/java/com/redhat/thermostat/client/internal/MenuRegistry.java	Tue Jun 19 15:37:59 2012 -0400
@@ -35,108 +35,18 @@
  */
 package com.redhat.thermostat.client.internal;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.concurrent.CopyOnWriteArrayList;
-
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
-import org.osgi.framework.FrameworkUtil;
 import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
-import org.osgi.util.tracker.ServiceTracker;
 
 import com.redhat.thermostat.client.osgi.service.MenuAction;
 
-public class MenuRegistry {
-
-    public static interface MenuListener {
-
-        public void added(String parentMenuName, MenuAction action);
-
-        public void removed(String parentMenuName, MenuAction action);
-    }
+public class MenuRegistry extends ThermostatExtensionRegistry<MenuAction> {
 
-    public static final String PARENT_MENU = "parentMenu";
-
-    private static final String FILTER = "(&(" + Constants.OBJECTCLASS + "=" + MenuAction.class.getName() + ")(" + PARENT_MENU + "=*))";
-
-    private ServiceTracker menuTracker;
-
-    private Map<String,List<MenuAction>> menus = new HashMap<>();
-    private List<MenuListener> listeners = new CopyOnWriteArrayList<>();
+    private static final String FILTER = "(" + Constants.OBJECTCLASS + "=" + MenuAction.class.getName() + ")";
 
     public MenuRegistry(BundleContext context) throws InvalidSyntaxException {
-        menuTracker = new ServiceTracker(context, FrameworkUtil.createFilter(FILTER), null) {
-            @Override
-            public Object addingService(ServiceReference reference) {
-                MenuAction action = (MenuAction) super.addingService(reference);
-                String parentMenuName = (String) reference.getProperty(PARENT_MENU);
-                menuAdded(parentMenuName, action);
-                return action;
-            }
-
-            @Override
-            public void removedService(ServiceReference reference, Object service) {
-                if (!(service instanceof MenuAction)) {
-                    throw new AssertionError("removing a non-MenuAction service");
-                }
-                String parentMenuName = (String) reference.getProperty(PARENT_MENU);
-                menuRemoved(parentMenuName, (MenuAction)service);
-                super.removedService(reference, service);
-            }
-        };
-    }
-
-    public void start() {
-        menuTracker.open();
-    }
-
-    public void stop() {
-        menuTracker.close();
-    }
-
-    public void addMenuListener(MenuListener listener) {
-        listeners.add(listener);
-
-        for (Entry<String,List<MenuAction>> entry: menus.entrySet()) {
-            for (MenuAction action: entry.getValue()) {
-                listener.added(entry.getKey(), action);
-            }
-        }
-    }
-
-    public void removeMenuListener(MenuListener listener) {
-        listeners.remove(listener);
-    }
-
-    private void menuAdded(String parentMenuName, MenuAction action) {
-        if (!menus.containsKey(parentMenuName)) {
-            menus.put(parentMenuName, new ArrayList<MenuAction>());
-        }
-        List<MenuAction> list = menus.get(parentMenuName);
-        list.add(action);
-        for (MenuListener listener: listeners) {
-            listener.added(parentMenuName, action);
-        }
-    }
-
-    private void menuRemoved(String parentMenuName, MenuAction action) {
-        if (!menus.containsKey(parentMenuName)) {
-            throw new IllegalArgumentException("unknown parent menu name");
-        }
-        List<MenuAction> list = menus.get(parentMenuName);
-        if (!list.contains(action)) {
-            throw new IllegalArgumentException("unknown menu action");
-        }
-
-        list.remove(action);
-        for (MenuListener listener: listeners) {
-            listener.removed(parentMenuName, action);
-        }
+    	super(context, FILTER, MenuAction.class);
     }
 
 }
--- a/client/core/src/main/java/com/redhat/thermostat/client/internal/ThermostatExtensionRegistry.java	Tue Jun 19 20:58:05 2012 +0200
+++ b/client/core/src/main/java/com/redhat/thermostat/client/internal/ThermostatExtensionRegistry.java	Tue Jun 19 15:37:59 2012 -0400
@@ -45,6 +45,9 @@
 import com.redhat.thermostat.common.ActionListener;
 import com.redhat.thermostat.common.ActionNotifier;
 
+/**
+ * A helper class to make it easier to implement registeries for osgi-services.
+ */
 public class ThermostatExtensionRegistry<E> {
 
     public enum Action {
@@ -93,7 +96,7 @@
         actionNotifier.addActionListener(l);
     }
 
-    public void removeViewActionListener(ActionListener<Action> l) {
+    public void removeActionListener(ActionListener<Action> l) {
         actionNotifier.removeActionListener(l);
     }
 }
--- a/client/core/src/main/java/com/redhat/thermostat/client/internal/VMTreeDecoratorRegistry.java	Tue Jun 19 20:58:05 2012 +0200
+++ b/client/core/src/main/java/com/redhat/thermostat/client/internal/VMTreeDecoratorRegistry.java	Tue Jun 19 15:37:59 2012 -0400
@@ -44,7 +44,7 @@
 
 class VMTreeDecoratorRegistry extends ThermostatExtensionRegistry<ReferenceDecorator> {
 
-    private static final String FILTER = "(&(" + Constants.OBJECTCLASS + "=" + ReferenceDecorator.class.getName() + "))";
+    private static final String FILTER = "(" + Constants.OBJECTCLASS + "=" + ReferenceDecorator.class.getName() + ")";
     
     public VMTreeDecoratorRegistry(BundleContext context) throws InvalidSyntaxException {
         super(context, FILTER, ReferenceDecorator.class);
--- a/client/core/src/main/java/com/redhat/thermostat/client/internal/VMTreeFilterRegistry.java	Tue Jun 19 20:58:05 2012 +0200
+++ b/client/core/src/main/java/com/redhat/thermostat/client/internal/VMTreeFilterRegistry.java	Tue Jun 19 15:37:59 2012 -0400
@@ -44,7 +44,7 @@
 
 class VMTreeFilterRegistry extends ThermostatExtensionRegistry<Filter> {
     
-    private static final String FILTER = "(&(" + Constants.OBJECTCLASS + "=" + Filter.class.getName() + "))";
+    private static final String FILTER = "(" + Constants.OBJECTCLASS + "=" + Filter.class.getName() + ")";
     
     public VMTreeFilterRegistry(BundleContext context) throws InvalidSyntaxException {
         super(context, FILTER, Filter.class);
--- a/client/core/src/main/java/com/redhat/thermostat/client/osgi/service/MenuAction.java	Tue Jun 19 20:58:05 2012 +0200
+++ b/client/core/src/main/java/com/redhat/thermostat/client/osgi/service/MenuAction.java	Tue Jun 19 15:37:59 2012 -0400
@@ -44,15 +44,17 @@
  */
 public interface MenuAction extends ContextAction {
 
-    public static enum TYPE {
+    public static enum Type {
         CHECK,
         RADIO,
         STANDARD
     };
-    
-    public static final String PARENT_MENU = "parentMenu";
-    
+
     /** Invoked when the user selects this menu item */
     void execute();
-    TYPE getType();
+
+    Type getType();
+
+    /** The path to the menu action. The last element must equal getName() */
+    String[] getPath();
 }
--- a/client/core/src/main/java/com/redhat/thermostat/client/ui/MainWindow.java	Tue Jun 19 20:58:05 2012 +0200
+++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/MainWindow.java	Tue Jun 19 15:37:59 2012 -0400
@@ -76,13 +76,11 @@
 import javax.swing.JMenuItem;
 import javax.swing.JPanel;
 import javax.swing.JPopupMenu;
-import javax.swing.JRadioButtonMenuItem;
 import javax.swing.JScrollPane;
 import javax.swing.JSplitPane;
 import javax.swing.JTextField;
 import javax.swing.JTree;
 import javax.swing.KeyStroke;
-import javax.swing.MenuElement;
 import javax.swing.SwingUtilities;
 import javax.swing.SwingWorker;
 import javax.swing.ToolTipManager;
@@ -104,10 +102,10 @@
 import com.redhat.thermostat.client.internal.HostsVMsLoader;
 import com.redhat.thermostat.client.internal.MainView;
 import com.redhat.thermostat.client.locale.LocaleResources;
+import com.redhat.thermostat.client.osgi.service.Filter;
 import com.redhat.thermostat.client.osgi.service.MenuAction;
 import com.redhat.thermostat.client.osgi.service.ReferenceDecorator;
 import com.redhat.thermostat.client.osgi.service.VMContextAction;
-import com.redhat.thermostat.client.osgi.service.Filter;
 import com.redhat.thermostat.common.ActionListener;
 import com.redhat.thermostat.common.ActionNotifier;
 import com.redhat.thermostat.common.dao.HostRef;
@@ -263,7 +261,8 @@
 
     private static final long serialVersionUID = 5608972421496808177L;
 
-    private final JMenuBar mainMenuBar;
+    private final JMenuBar mainMenuBar = new JMenuBar();
+    private final MenuHelper mainMenuHelper = new MenuHelper(mainMenuBar);
     private JPanel contentArea = null;
 
     private JTextField searchField = null;
@@ -294,8 +293,6 @@
         ToolTipManager.sharedInstance().registerComponent(agentVmTree);
         contentArea = new JPanel(new BorderLayout());
 
-        mainMenuBar = new JMenuBar();
-
         setupMenus();
         setupPanels();
 
@@ -732,108 +729,17 @@
             }
         });
     }
-    
+
     @Override
-    public void addMenu(final String parentMenuName, final MenuAction action) {
-        SwingUtilities.invokeLater(new Runnable() {
-            @Override
-            public void run() {
-                JMenu parent = null;
-                int mainMenuCount = mainMenuBar.getMenuCount();
-                for (int i = 0; i < mainMenuCount; i++) {
-                    if (mainMenuBar.getMenu(i).getText().equals(parentMenuName)) {
-                        parent = mainMenuBar.getMenu(i);
-                        break;
-                    }
-                }
-                if (parent == null) {
-                    parent = new JMenu(parentMenuName);
-                    mainMenuBar.add(parent);
-                }
-
-                JMenuItem menu = null;
-                switch (action.getType()) {
-                case RADIO:
-                    menu = new JRadioButtonMenuItem();                    
-                    break;
-                case CHECK:
-                    menu = new JCheckBoxMenuItem();
-                    break;
-                    
-                case STANDARD:
-                default:
-                    menu = new JMenuItem();
-                    break;
-                }
-                
-                menu.setText(action.getName());
-                menu.addActionListener(new java.awt.event.ActionListener() {
-                    @Override
-                    public void actionPerformed(ActionEvent e) {
-                        action.execute();
-                    }
-                });
-                parent.add(menu);
-
-                mainMenuBar.revalidate();
-            }
-        });
+    public void addMenu(MenuAction action) {
+        mainMenuHelper.addMenuAction(action);
     }
 
     @Override
-    public void removeMenu(final String parentMenuName, final  MenuAction action) {
-        final String actionName = action.getName();
-        try {
-            new EdtHelper().callAndWait(new Runnable() {
-                @Override
-                public void run() {
-                    MenuElement parent = null;
-                    int mainMenuCount = mainMenuBar.getMenuCount();
-                    for (int i = 0; i < mainMenuCount; i++) {
-                        if (mainMenuBar.getMenu(i).getText().equals(parentMenuName)) {
-                            parent = mainMenuBar.getMenu(i);
-                            break;
-                        }
-                    }
-                    if (parent == null) {
-                        throw new IllegalArgumentException("parent menu not found");
-                    }
-
-                    boolean removed = false;
-                    MenuElement[] menus = parent.getSubElements();
-                    if (menus.length == 1 && (menus[0] instanceof JPopupMenu)) {
-                        parent = menus[0];
-                        menus = parent.getSubElements();
-                    }
+    public void removeMenu(MenuAction action) {
+        mainMenuHelper.removeMenuAction(action);
+    }
 
-                    for (MenuElement menu: menus) {
-                        if (menu instanceof JMenuItem && ((JMenuItem)menu).getText().equals(actionName)) {
-                            if (parent instanceof JPopupMenu) {
-                                ((JPopupMenu)parent).remove((JMenuItem)menu);
-                                removed = true;
-                            }
-                        }
-                    }
-
-                    if (!removed) {
-                        throw new IllegalArgumentException("child menu not found");
-                    }
-
-                    mainMenuBar.revalidate();
-                }
-            });
-        } catch (InterruptedException ie) {
-            Thread.currentThread().interrupt();
-            throw new RuntimeException(ie);
-        } catch (InvocationTargetException roe) {
-            Throwable cause = roe.getCause();
-            if (cause instanceof IllegalArgumentException) {
-                throw (IllegalArgumentException) cause;
-            }
-            throw new RuntimeException(cause);
-        }
-    }
-    
     /**
      * Returns null to indicate no Ref is selected
      */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/MenuHelper.java	Tue Jun 19 15:37:59 2012 -0400
@@ -0,0 +1,286 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING. If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code. Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module. An independent module is a module
+ * which is not derived from or based on this code. If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so. If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.ui;
+
+import java.awt.event.ActionEvent;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
+import java.util.logging.Logger;
+
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.JRadioButtonMenuItem;
+import javax.swing.MenuElement;
+
+import com.redhat.thermostat.client.osgi.service.MenuAction;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.common.utils.StringUtils;
+
+public class MenuHelper {
+
+    private static final Logger logger = LoggingUtils.getLogger(MenuHelper.class);
+
+    private final JMenuBar menuBar;
+
+    public MenuHelper(JMenuBar menuBar) {
+        this.menuBar = menuBar;
+    }
+
+    /**
+     * Add a menu item as specified though the {@link MenuAction} argument.
+     */
+    public void addMenuAction(final MenuAction action) {
+        try {
+            new EdtHelper().callAndWait(new Runnable() {
+                @Override
+                public void run() {
+                    String[] path = action.getPath();
+                    Menu parent = findMenuParent(menuBar, path, true);
+                    JMenuItem menu = null;
+                    switch (action.getType()) {
+                    case RADIO:
+                        menu = new JRadioButtonMenuItem();
+                        break;
+                    case CHECK:
+                        menu = new JCheckBoxMenuItem();
+                        break;
+
+                    case STANDARD:
+                    default:
+                        menu = new JMenuItem();
+                        break;
+                    }
+
+                    menu.setText(action.getName());
+                    menu.addActionListener(new java.awt.event.ActionListener() {
+                        @Override
+                        public void actionPerformed(ActionEvent e) {
+                            action.execute();
+                        }
+                    });
+                    parent.add(new Menu(menu));
+
+                    menuBar.revalidate();
+                }
+            });
+        } catch (InvocationTargetException ite) {
+            Throwable cause = ite.getCause();
+            if (cause instanceof IllegalArgumentException) {
+                throw (IllegalArgumentException) cause;
+            }
+            throw new RuntimeException(cause);
+        } catch (InterruptedException ie) {
+            Thread.currentThread().interrupt();
+            throw new RuntimeException(ie);
+        }
+
+    }
+
+    /**
+     * Remove a existing menu item as specified though the {@link MenuAction}
+     * argument.
+     *
+     * @throws IllegalArgumentException if the path specified in
+     * {@link MenuAction#getPath()} can not be found
+     */
+    public void removeMenuAction(final MenuAction action) {
+        try {
+            new EdtHelper().callAndWait(new Runnable() {
+                @Override
+                public void run() {
+                    String[] path = action.getPath();
+                    Menu parent = findMenuParent(menuBar, path, false);
+                    parent.remove(path[path.length - 1]);
+                    menuBar.revalidate();
+                }
+            });
+        } catch (InterruptedException ie) {
+            Thread.currentThread().interrupt();
+            throw new RuntimeException(ie);
+        } catch (InvocationTargetException roe) {
+            Throwable cause = roe.getCause();
+            if (cause instanceof IllegalArgumentException) {
+                throw (IllegalArgumentException) cause;
+            }
+            throw new RuntimeException(cause);
+        }
+
+    }
+
+    private static Menu findMenuParent(JMenuBar menuBar, String[] path, boolean createIfNotFound) {
+        Menu parent = null;
+        int mainMenuCount = menuBar.getMenuCount();
+        for (int i = 0; i < mainMenuCount; i++) {
+            JMenu menu = menuBar.getMenu(i);
+            if (menu.getText().equals(path[0])) {
+                parent = new Menu(menuBar.getMenu(i));
+                break;
+            }
+        }
+        if (parent == null) {
+            if (createIfNotFound) {
+                JMenu delegate = new JMenu(path[0]);
+                parent = new Menu(delegate);
+                menuBar.add(delegate);
+            } else {
+                throw new IllegalArgumentException("top-level " + path[0] + " not found (using path" + Arrays.toString(path) + ")");
+            }
+        }
+
+        for (int i = 1; i < path.length - 1; i++) {
+            Menu[] children = parent.children();
+            boolean found = false;
+            for (int j = 0; j < children.length; j++) {
+                Menu menu = children[j];
+                if (menu.getText().equals(path[i])) {
+                    parent = menu;
+                    found = true;
+                }
+            }
+            if (!found) {
+                if (createIfNotFound) {
+                    Menu newMenu = new Menu(new JMenu(path[i]));
+                    parent.add(newMenu);
+                    parent = newMenu;
+                } else {
+                    throw new IllegalArgumentException("path not found");
+                }
+            }
+        }
+
+        return parent;
+    }
+
+    private static String getText(MenuElement element) {
+        if (element instanceof JMenuItem) {
+            return ((JMenuItem) element).getText();
+        }
+        return "";
+    }
+
+    @SuppressWarnings("unused") // this method is for debugging only
+    private static void printMenu(MenuElement parent, int nestingLevel) {
+        System.out.println(StringUtils.repeat(" ", nestingLevel * 2) + getText(parent) + " [" + parent.getClass() + "]");
+        for (MenuElement element : parent.getSubElements()) {
+            printMenu(element, nestingLevel + 1);
+        }
+    }
+
+    /**
+     * The swing menu hierarchy makes uniform operations very difficult. This
+     * is a hack around that.
+     */
+    private static final class Menu {
+        private Object swingDelegate;
+
+        public Menu() { /* no op */}
+
+        public Menu(JMenuItem actual) {
+            this.swingDelegate = actual;
+        }
+
+        public String getText() {
+            if (swingDelegate instanceof JMenuItem) {
+                return ((JMenuItem) swingDelegate).getText();
+            }
+            return null;
+        }
+
+        public Menu[] children() {
+            if (swingDelegate instanceof MenuElement) {
+                MenuElement[] actualChildren = ((MenuElement) swingDelegate).getSubElements();
+                if (actualChildren.length == 1 && actualChildren[0] instanceof JPopupMenu) {
+                    actualChildren = actualChildren[0].getSubElements();
+                }
+
+                Menu[] children = new Menu[actualChildren.length];
+                for (int i = 0; i < children.length; i++) {
+                    children[i] = new Menu((JMenuItem) actualChildren[i]);
+                }
+                return children;
+            }
+            return new Menu[0];
+        }
+
+        public void add(Menu menu) {
+            if (swingDelegate instanceof JPopupMenu) {
+                ((JPopupMenu) swingDelegate).add((JMenuItem) menu.swingDelegate);
+            } else if (swingDelegate instanceof JMenu) {
+                ((JMenu) swingDelegate).add((JMenuItem) menu.swingDelegate);
+            } else {
+                logger.warning("Unable to add menu. Menu is of unrecognized type: " + menu.swingDelegate);
+            }
+        }
+
+        public void remove(String string) {
+            JPopupMenu removeParent = null;
+
+            if (swingDelegate instanceof JMenu) {
+                JMenu parent = (JMenu) swingDelegate;
+                MenuElement[] actualChildren = parent.getSubElements();
+                if (actualChildren.length == 1 && actualChildren[0] instanceof JPopupMenu) {
+                    removeParent = (JPopupMenu) actualChildren[0];
+                }
+            }
+
+            if (removeParent == null) {
+                if (swingDelegate instanceof JPopupMenu) {
+                    removeParent = (JPopupMenu) swingDelegate;
+                } else {
+                    logger.warning("BUG: problem while removing menu. delegate is not a JPopupMenu, cant remove");
+                    return;
+                }
+            }
+
+            JPopupMenu parent = removeParent;
+            for (MenuElement element : parent.getSubElements()) {
+                if (((JMenuItem) element).getText().equals(string)) {
+                    parent.remove((JMenuItem) element);
+                }
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "Menu [" + swingDelegate.toString() + "]";
+        }
+    }
+
+}
--- a/client/core/src/test/java/com/redhat/thermostat/client/internal/MainWindowControllerImplTest.java	Tue Jun 19 20:58:05 2012 +0200
+++ b/client/core/src/test/java/com/redhat/thermostat/client/internal/MainWindowControllerImplTest.java	Tue Jun 19 15:37:59 2012 -0400
@@ -76,6 +76,7 @@
 import com.redhat.thermostat.client.internal.UiFacadeFactory;
 import com.redhat.thermostat.client.internal.VMTreeDecoratorRegistry;
 import com.redhat.thermostat.client.internal.VMTreeFilterRegistry;
+import com.redhat.thermostat.client.internal.ThermostatExtensionRegistry.Action;
 import com.redhat.thermostat.client.osgi.service.Filter;
 import com.redhat.thermostat.client.osgi.service.MenuAction;
 import com.redhat.thermostat.client.osgi.service.ReferenceDecorator;
@@ -120,7 +121,7 @@
     private VMTreeFilterRegistry filters;
     private VMTreeDecoratorRegistry decorators;
     private VMInformationRegistry vmInfoRegistry;
-    private MenuRegistry menues;
+    private MenuRegistry menus;
     
     private ActionListener<ThermostatExtensionRegistry.Action> filtersListener;
     private ActionListener<ThermostatExtensionRegistry.Action> decoratorsListener;
@@ -158,9 +159,9 @@
         filters = mock(VMTreeFilterRegistry.class);
         decorators = mock(VMTreeDecoratorRegistry.class);
         vmInfoRegistry = mock(VMInformationRegistry.class);
-        menues = mock(MenuRegistry.class);
+        menus = mock(MenuRegistry.class);
 
-        when(registryFactory.createMenuRegistry()).thenReturn(menues);
+        when(registryFactory.createMenuRegistry()).thenReturn(menus);
         when(registryFactory.createVMTreeDecoratorRegistry()).thenReturn(decorators);
         when(registryFactory.createVMTreeFilterRegistry()).thenReturn(filters);
         when(registryFactory.createVMInformationRegistry()).thenReturn(vmInfoRegistry);
@@ -519,16 +520,21 @@
     @Test
     public void verifyMenuItems() {
         
-        MenuRegistry.MenuListener menuListener = controller.getMenuListener();
+        ActionListener<ThermostatExtensionRegistry.Action> menuListener = controller.getMenuListener();
 
         MenuAction action = mock(MenuAction.class);
         when(action.getName()).thenReturn("Test1");
 
-        menuListener.added("File", action);
-        verify(view).addMenu("File", action);
+        ActionEvent<Action> addEvent = new ActionEvent<ThermostatExtensionRegistry.Action>(
+        		menus, ThermostatExtensionRegistry.Action.SERVICE_ADDED);
+        addEvent.setPayload(action);
+        menuListener.actionPerformed(addEvent);
+        verify(view).addMenu(action);
 
-        menuListener.removed("File", action);
-        verify(view).removeMenu("File", action);
+        ActionEvent<Action> removeEvent = new ActionEvent<ThermostatExtensionRegistry.Action>(menus, ThermostatExtensionRegistry.Action.SERVICE_REMOVED);
+        removeEvent.setPayload(action);
+        menuListener.actionPerformed(removeEvent);
+        verify(view).removeMenu(action);
     }
 
    @Test
--- a/client/core/src/test/java/com/redhat/thermostat/client/internal/MenuRegistryTest.java	Tue Jun 19 20:58:05 2012 +0200
+++ b/client/core/src/test/java/com/redhat/thermostat/client/internal/MenuRegistryTest.java	Tue Jun 19 15:37:59 2012 -0400
@@ -36,10 +36,10 @@
 
 package com.redhat.thermostat.client.internal;
 
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.isA;
+import static org.junit.Assert.*;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -52,8 +52,9 @@
 import org.osgi.framework.ServiceReference;
 
 import com.redhat.thermostat.client.internal.MenuRegistry;
-import com.redhat.thermostat.client.internal.MenuRegistry.MenuListener;
 import com.redhat.thermostat.client.osgi.service.MenuAction;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
 
 public class MenuRegistryTest {
 
@@ -62,7 +63,7 @@
         ArgumentCaptor<ServiceListener> serviceListenerCaptor = ArgumentCaptor.forClass(ServiceListener.class);
         ArgumentCaptor<String> filterCaptor = ArgumentCaptor.forClass(String.class);
 
-        MenuListener menuListener = mock(MenuListener.class);
+        ActionListener<ThermostatExtensionRegistry.Action> menuListener = mock(ActionListener.class);
         MenuAction menuAction = mock(MenuAction.class);
 
         BundleContext context = mock(BundleContext.class);
@@ -70,22 +71,26 @@
 
         ServiceReference ref = mock(ServiceReference.class);
         when(ref.getProperty("objectClass")).thenReturn(MenuAction.class.getName());
-        when(ref.getProperty(MenuRegistry.PARENT_MENU)).thenReturn("Test");
 
         when(context.getService(ref)).thenReturn(menuAction);
 
         MenuRegistry registry = new MenuRegistry(context);
-        registry.addMenuListener(menuListener);
+        registry.addActionListener(menuListener);
         registry.start();
 
         ServiceListener serviceListener = serviceListenerCaptor.getValue();
         serviceListener.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, ref));
 
-        verify(menuListener).added(eq("Test"), isA(MenuAction.class));
-
+        ArgumentCaptor<ActionEvent> eventCaptor = ArgumentCaptor.forClass(ActionEvent.class);
         serviceListener.serviceChanged(new ServiceEvent(ServiceEvent.UNREGISTERING, ref));
 
-        verify(menuListener).removed(eq("Test"), isA(MenuAction.class));
+        verify(menuListener, times(2)).actionPerformed(eventCaptor.capture());
+
+        ActionEvent firstEvent = eventCaptor.getAllValues().get(0);
+        ActionEvent secondEvent = eventCaptor.getAllValues().get(1);
+
+        assertEquals(ThermostatExtensionRegistry.Action.SERVICE_ADDED, firstEvent.getActionId());
+        assertEquals(ThermostatExtensionRegistry.Action.SERVICE_REMOVED, secondEvent.getActionId());
 
     }
 }
--- a/client/core/src/test/java/com/redhat/thermostat/client/ui/MainWindowTest.java	Tue Jun 19 20:58:05 2012 +0200
+++ b/client/core/src/test/java/com/redhat/thermostat/client/ui/MainWindowTest.java	Tue Jun 19 15:37:59 2012 -0400
@@ -252,27 +252,29 @@
     @Category(GUITest.class)
     @Test
     public void addRemoveMenu() {
-        final String MENU_NAME = "Test";
+    	final String PARENT_NAME = "File";
+        final String MENU_NAME = "Test2";
         MenuAction action = mock(MenuAction.class);
         when(action.getName()).thenReturn(MENU_NAME);
-        when(action.getType()).thenReturn(MenuAction.TYPE.STANDARD);
+        when(action.getPath()).thenReturn(new String[] {PARENT_NAME, MENU_NAME});
+        when(action.getType()).thenReturn(MenuAction.Type.STANDARD);
 
         JMenuItemFixture menuItem;
 
         frameFixture.show();
 
-        window.addMenu("File", action);
+        window.addMenu(action);
 
-        menuItem = frameFixture.menuItemWithPath("File", MENU_NAME);
+        menuItem = frameFixture.menuItemWithPath(PARENT_NAME, MENU_NAME);
         assertNotNull(menuItem);
         menuItem.click();
 
         verify(action).execute();
 
-        window.removeMenu("File", action);
+        window.removeMenu(action);
 
         try {
-            menuItem = frameFixture.menuItemWithPath("File", MENU_NAME);
+            menuItem = frameFixture.menuItemWithPath(PARENT_NAME, MENU_NAME);
             // should not reach here
             assertTrue(false);
         } catch (ComponentLookupException cle) {
@@ -283,18 +285,22 @@
     @Category(GUITest.class)
     @Test
     public void addRadioMenu() {
+    	final String PARENT_NAME = "File";
         final String MENU_NAME = "Test";
         MenuAction action = mock(MenuAction.class);
         when(action.getName()).thenReturn(MENU_NAME);
-        when(action.getType()).thenReturn(MenuAction.TYPE.RADIO);
+        when(action.getPath()).thenReturn(new String[] {PARENT_NAME, MENU_NAME});
+
+
+        when(action.getType()).thenReturn(MenuAction.Type.RADIO);
 
         JMenuItemFixture menuItem;
 
         frameFixture.show();
 
-        window.addMenu("File", action);
+        window.addMenu(action);
 
-        menuItem = frameFixture.menuItemWithPath("File", MENU_NAME);
+        menuItem = frameFixture.menuItemWithPath(PARENT_NAME, MENU_NAME);
         assertNotNull(menuItem);
 
         assertTrue(menuItem.target instanceof JRadioButtonMenuItem);
@@ -303,18 +309,21 @@
     @Category(GUITest.class)
     @Test
     public void addCheckBoxMenu() {
+    	final String PARENT_NAME = "File";
         final String MENU_NAME = "Test";
         MenuAction action = mock(MenuAction.class);
         when(action.getName()).thenReturn(MENU_NAME);
-        when(action.getType()).thenReturn(MenuAction.TYPE.CHECK);
+        when(action.getType()).thenReturn(MenuAction.Type.CHECK);
+        when(action.getPath()).thenReturn(new String[] {PARENT_NAME, MENU_NAME});
+
 
         JMenuItemFixture menuItem;
 
         frameFixture.show();
 
-        window.addMenu("File", action);
+        window.addMenu(action);
 
-        menuItem = frameFixture.menuItemWithPath("File", MENU_NAME);
+        menuItem = frameFixture.menuItemWithPath(PARENT_NAME, MENU_NAME);
         assertNotNull(menuItem);
 
         assertTrue(menuItem.target instanceof JCheckBoxMenuItem);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/core/src/test/java/com/redhat/thermostat/client/ui/MenuHelperTest.java	Tue Jun 19 15:37:59 2012 -0400
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING. If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code. Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module. An independent module is a module
+ * which is not derived from or based on this code. If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so. If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.ui;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JRadioButtonMenuItem;
+
+import net.java.openjdk.cacio.ctc.junit.CacioFESTRunner;
+
+import org.fest.swing.annotation.GUITest;
+import org.fest.swing.edt.FailOnThreadViolationRepaintManager;
+import org.fest.swing.edt.GuiActionRunner;
+import org.fest.swing.edt.GuiTask;
+import org.fest.swing.exception.ComponentLookupException;
+import org.fest.swing.fixture.FrameFixture;
+import org.fest.swing.fixture.JMenuItemFixture;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import com.redhat.thermostat.client.osgi.service.MenuAction;
+
+@RunWith(CacioFESTRunner.class)
+public class MenuHelperTest {
+
+    private FrameFixture frameFixture;
+    private JFrame window;
+    private MenuHelper menu;
+
+    @BeforeClass
+    public static void setUpOnce() {
+        FailOnThreadViolationRepaintManager.install();
+    }
+
+    @Before
+    public void setUp() {
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                window = new JFrame();
+                JMenuBar menuBar = new JMenuBar();
+                window.setJMenuBar(menuBar);
+                JMenu fileMenu = new JMenu("File");
+                fileMenu.setName("File");
+                menuBar.add(fileMenu);
+                menu = new MenuHelper(menuBar);
+                window.pack();
+            }
+        });
+
+        frameFixture = new FrameFixture(window);
+    }
+
+    @After
+    public void tearDown() {
+        frameFixture.cleanUp();
+        frameFixture = null;
+        window = null;
+    }
+
+    @Category(GUITest.class)
+    @Test
+    public void addRemoveWithNewTopLevelMenu() {
+        final String PARENT_NAME = "Test1";
+        final String MENU_NAME = "Test2";
+        MenuAction action = mock(MenuAction.class);
+        when(action.getName()).thenReturn(MENU_NAME);
+        when(action.getPath()).thenReturn(new String[] { PARENT_NAME, MENU_NAME });
+        when(action.getType()).thenReturn(MenuAction.Type.STANDARD);
+
+        JMenuItemFixture menuItem;
+
+        frameFixture.show();
+
+        menu.addMenuAction(action);
+
+        menuItem = frameFixture.menuItemWithPath(PARENT_NAME, MENU_NAME);
+        assertNotNull(menuItem);
+
+        menu.removeMenuAction(action);
+
+        try {
+            menuItem = frameFixture.menuItemWithPath(PARENT_NAME, MENU_NAME);
+            // should not reach here
+            assertTrue(false);
+        } catch (ComponentLookupException cle) {
+            // expected
+        }
+    }
+
+    @Category(GUITest.class)
+    @Test
+    public void addRemoveToExistingMenu() {
+        final String PARENT_NAME = "File";
+        final String MENU_NAME = "Test2";
+        MenuAction action = mock(MenuAction.class);
+        when(action.getName()).thenReturn(MENU_NAME);
+        when(action.getPath()).thenReturn(new String[] { PARENT_NAME, MENU_NAME });
+        when(action.getType()).thenReturn(MenuAction.Type.STANDARD);
+
+        JMenuItemFixture menuItem;
+
+        frameFixture.show();
+
+        assertNotNull(frameFixture.menuItem("File"));
+
+        menu.addMenuAction(action);
+
+        menuItem = frameFixture.menuItemWithPath(PARENT_NAME, MENU_NAME);
+        assertNotNull(menuItem);
+
+        menu.removeMenuAction(action);
+
+        try {
+            menuItem = frameFixture.menuItemWithPath(PARENT_NAME, MENU_NAME);
+            // should not reach here
+            assertTrue(false);
+        } catch (ComponentLookupException cle) {
+            // expected
+        }
+    }
+
+    @Category(GUITest.class)
+    @Test
+    public void addRemoveHighlyNextedMenu() {
+        final String[] path = new String[] { "View", "Filter", "Virtual Machine", "Show Only Running" };
+        MenuAction action = mock(MenuAction.class);
+        when(action.getName()).thenReturn(path[path.length - 1]);
+        when(action.getPath()).thenReturn(path);
+        when(action.getType()).thenReturn(MenuAction.Type.STANDARD);
+
+        JMenuItemFixture menuItem;
+
+        frameFixture.show();
+
+        menu.addMenuAction(action);
+
+        menuItem = frameFixture.menuItemWithPath(path);
+        assertNotNull(menuItem);
+
+        menu.removeMenuAction(action);
+
+        try {
+            menuItem = frameFixture.menuItemWithPath(path);
+            // should not reach here
+            assertTrue(false);
+        } catch (ComponentLookupException cle) {
+            // expected
+        }
+    }
+
+    @Category(GUITest.class)
+    @Test
+    public void addRadioMenu() {
+        final String PARENT_NAME = "File";
+        final String MENU_NAME = "Test";
+        MenuAction action = mock(MenuAction.class);
+        when(action.getName()).thenReturn(MENU_NAME);
+        when(action.getPath()).thenReturn(new String[] { PARENT_NAME, MENU_NAME });
+        when(action.getType()).thenReturn(MenuAction.Type.RADIO);
+
+        JMenuItemFixture menuItem;
+
+        frameFixture.show();
+
+        menu.addMenuAction(action);
+
+        menuItem = frameFixture.menuItemWithPath(PARENT_NAME, MENU_NAME);
+        assertNotNull(menuItem);
+
+        assertTrue(menuItem.target instanceof JRadioButtonMenuItem);
+    }
+
+    @Category(GUITest.class)
+    @Test
+    public void addCheckBoxMenu() {
+        final String PARENT_NAME = "File";
+        final String MENU_NAME = "Test";
+        MenuAction action = mock(MenuAction.class);
+        when(action.getName()).thenReturn(MENU_NAME);
+        when(action.getType()).thenReturn(MenuAction.Type.CHECK);
+        when(action.getPath()).thenReturn(new String[] { PARENT_NAME, MENU_NAME });
+
+        JMenuItemFixture menuItem;
+
+        frameFixture.show();
+
+        menu.addMenuAction(action);
+
+        menuItem = frameFixture.menuItemWithPath(PARENT_NAME, MENU_NAME);
+        assertNotNull(menuItem);
+
+        assertTrue(menuItem.target instanceof JCheckBoxMenuItem);
+    }
+
+}
--- a/client/living-vm-filter/src/main/java/com/redhat/thermostat/client/filter/vm/LivingVMFilterMenuAction.java	Tue Jun 19 20:58:05 2012 +0200
+++ b/client/living-vm-filter/src/main/java/com/redhat/thermostat/client/filter/vm/LivingVMFilterMenuAction.java	Tue Jun 19 15:37:59 2012 -0400
@@ -40,8 +40,6 @@
 
 class LivingVMFilterMenuAction implements MenuAction {
 
-    public static final String PARENT_MENU = "Edit";
-
     private LivingVMFilter filter;
     
     public LivingVMFilterMenuAction(LivingVMFilter filter) {
@@ -64,7 +62,12 @@
     }
 
     @Override
-    public TYPE getType() {
-        return TYPE.CHECK;
+    public Type getType() {
+        return Type.CHECK;
+    }
+
+    @Override
+    public String[] getPath() {
+        return new String[] { "Edit", getName() };
     }
 }
--- a/client/living-vm-filter/src/main/java/com/redhat/thermostat/client/filter/vm/VMFilterActivator.java	Tue Jun 19 20:58:05 2012 +0200
+++ b/client/living-vm-filter/src/main/java/com/redhat/thermostat/client/filter/vm/VMFilterActivator.java	Tue Jun 19 15:37:59 2012 -0400
@@ -56,9 +56,6 @@
         ServiceTracker tracker = new ServiceTracker(context, ApplicationService.class.getName(), null) {
             @Override
             public Object addingService(ServiceReference reference) {
-                Hashtable<String, String> props = new Hashtable<>();
-                props.put(MenuAction.PARENT_MENU, LivingVMFilterMenuAction.PARENT_MENU);
-                
                 ApplicationService service = (ApplicationService) context.getService(reference);
                 
                 LivingVMFilter filter = new LivingVMFilter(service.getDAOFactory());
@@ -71,7 +68,7 @@
                 context.registerService(ReferenceDecorator.class.getName(), decorator, null);
                 
                 context.registerService(Filter.class.getName(), filter, null);
-                context.registerService(MenuAction.class.getName(), menu, props);
+                context.registerService(MenuAction.class.getName(), menu, null);
                 
                 return super.addingService(reference);
             }