Mercurial > hg > release > thermostat-0.4
changeset 339:cd786147af62
Merge
author | Roman Kennke <rkennke@redhat.com> |
---|---|
date | Wed, 30 May 2012 20:16:19 +0200 |
parents | eae8d583ebd4 (current diff) 9368bea3c19c (diff) |
children | d4850c83bbc1 |
files | tools/src/main/java/com/redhat/thermostat/tools/cli/VMStatCommand.java |
diffstat | 37 files changed, 910 insertions(+), 91 deletions(-) [+] |
line wrap: on
line diff
--- a/agent/src/main/java/com/redhat/thermostat/agent/Activator.java Wed May 30 20:14:55 2012 +0200 +++ b/agent/src/main/java/com/redhat/thermostat/agent/Activator.java Wed May 30 20:16:19 2012 +0200 @@ -37,30 +37,26 @@ package com.redhat.thermostat.agent; import java.util.Arrays; -import java.util.Collection; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceRegistration; import com.redhat.thermostat.common.cli.CommandRegistry; import com.redhat.thermostat.common.cli.CommandRegistryImpl; public class Activator implements BundleActivator { - private Collection<ServiceRegistration> agentCmd; + private CommandRegistry reg; @Override public void start(BundleContext context) throws Exception { - CommandRegistry reg = new CommandRegistryImpl(context); - agentCmd = reg.registerCommands(Arrays.asList(new AgentApplication())); + reg = new CommandRegistryImpl(context); + reg.registerCommands(Arrays.asList(new AgentApplication())); } @Override public void stop(BundleContext context) throws Exception { - for (ServiceRegistration reg : agentCmd) { - reg.unregister(); - } + reg.unregisterCommands(); } }
--- a/agent/src/main/java/com/redhat/thermostat/agent/AgentApplication.java Wed May 30 20:14:55 2012 +0200 +++ b/agent/src/main/java/com/redhat/thermostat/agent/AgentApplication.java Wed May 30 20:16:19 2012 +0200 @@ -178,6 +178,11 @@ } @Override + public void disable() { + /* NO-OP */ + } + + @Override public String getName() { return NAME; }
--- a/client/core/src/main/java/com/redhat/thermostat/client/GUIClientCommand.java Wed May 30 20:14:55 2012 +0200 +++ b/client/core/src/main/java/com/redhat/thermostat/client/GUIClientCommand.java Wed May 30 20:16:19 2012 +0200 @@ -69,6 +69,11 @@ } @Override + public void disable() { + // TODO clientMain.shutdown(); + } + + @Override public String getName() { return "gui"; }
--- a/client/core/src/main/java/com/redhat/thermostat/client/MainView.java Wed May 30 20:14:55 2012 +0200 +++ b/client/core/src/main/java/com/redhat/thermostat/client/MainView.java Wed May 30 20:16:19 2012 +0200 @@ -37,7 +37,10 @@ package com.redhat.thermostat.client; import java.awt.Component; +import java.awt.event.MouseEvent; +import java.util.List; +import com.redhat.thermostat.client.osgi.service.MenuAction; import com.redhat.thermostat.client.osgi.service.VMContextAction; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.dao.Ref; @@ -54,6 +57,7 @@ SWITCH_HISTORY_MODE, SHOW_ABOUT_DIALOG, SHUTDOWN, + SHOW_VM_CONTEXT_MENU, VM_CONTEXT_ACTION, } @@ -73,5 +77,9 @@ void setSubView(Component view); - void registerVMContextAction(VMContextAction action); + void addMenu(String parentMenuName, MenuAction action); + + void removeMenu(String parentMenuName, MenuAction action); + + void showVMContextActions(List<VMContextAction> actions, MouseEvent e); }
--- a/client/core/src/main/java/com/redhat/thermostat/client/MainWindowControllerImpl.java Wed May 30 20:14:55 2012 +0200 +++ b/client/core/src/main/java/com/redhat/thermostat/client/MainWindowControllerImpl.java Wed May 30 20:16:19 2012 +0200 @@ -36,13 +36,16 @@ package com.redhat.thermostat.client; +import java.awt.event.MouseEvent; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; -import org.osgi.framework.BundleException; - +import com.redhat.thermostat.client.MainView.Action; +import com.redhat.thermostat.client.osgi.service.MenuAction; import com.redhat.thermostat.client.osgi.service.VMContextAction; import com.redhat.thermostat.client.ui.AboutDialog; import com.redhat.thermostat.client.ui.AgentConfigurationController; @@ -65,6 +68,7 @@ import com.redhat.thermostat.common.dao.Ref; import com.redhat.thermostat.common.dao.VmInfoDAO; import com.redhat.thermostat.common.dao.VmRef; +import com.redhat.thermostat.common.model.VmInfo; import com.redhat.thermostat.common.utils.LoggingUtils; public class MainWindowControllerImpl implements MainWindowController { @@ -83,13 +87,27 @@ private ApplicationInfo appInfo; private UiFacadeFactory facadeFactory; + private MenuRegistry menuRegistry; + private MenuRegistry.MenuListener menuListener = new MenuRegistry.MenuListener() { + + @Override + public void removed(String parentMenuName, MenuAction action) { + view.removeMenu(parentMenuName, action); + } + + @Override + public void added(String parentMenuName, MenuAction action) { + view.addMenu(parentMenuName, action); + } + }; private boolean showHistory; private VmInformationControllerProvider vmInfoControllerProvider; - - public MainWindowControllerImpl(UiFacadeFactory facadeFactory, MainView view) { + + public MainWindowControllerImpl(UiFacadeFactory facadeFactory, MainView view, MenuRegistry menuRegistry) { this.facadeFactory = facadeFactory; + this.menuRegistry = menuRegistry; ApplicationContext ctx = ApplicationContext.getInstance(); DAOFactory daoFactory = ctx.getDAOFactory(); @@ -104,12 +122,10 @@ view.setWindowTitle(appInfo.getName()); initializeTimer(); - logger.log(Level.INFO, "registering VMContextActions actions to view"); - for (VMContextAction action : facadeFactory.getVMContextActions()) { - view.registerVMContextAction(action); - } - updateView(); + + menuRegistry.start(); + menuRegistry.addMenuListener(menuListener); } private class HostsVMsLoaderImpl implements HostsVMsLoader { @@ -197,6 +213,9 @@ case SHOW_ABOUT_DIALOG: showAboutDialog(); break; + case SHOW_VM_CONTEXT_MENU: + showContextMenu(evt); + break; case VM_CONTEXT_ACTION: handleVMHooks(evt); break; @@ -212,6 +231,10 @@ } private void shutdownApplication() { + menuRegistry.removeMenuListener(menuListener); + menuListener = null; + menuRegistry.stop(); + view.hideMainWindow(); ApplicationContext.getInstance().getTimerFactory().shutdown(); shutdownOSGiFramework(); @@ -221,6 +244,21 @@ facadeFactory.shutdown(); } + private void showContextMenu(ActionEvent<Action> evt) { + List<VMContextAction> toShow = new ArrayList<>(); + VmRef vm = (VmRef) view.getSelectedHostOrVm(); + + logger.log(Level.INFO, "registering applicable VMContextActions actions to show"); + + for (VMContextAction action : facadeFactory.getVMContextActions()) { + if (action.getFilter().matches(vm)) { + toShow.add(action); + } + } + + view.showVMContextActions(toShow, (MouseEvent)evt.getPayload()); + } + private void handleVMHooks(ActionEvent<MainView.Action> event) { Object payload = event.getPayload(); if (payload instanceof VMContextAction) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/core/src/main/java/com/redhat/thermostat/client/MenuRegistry.java Wed May 30 20:16:19 2012 +0200 @@ -0,0 +1,142 @@ +/* + * 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; + +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 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<>(); + + 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); + } + } + +}
--- a/client/core/src/main/java/com/redhat/thermostat/client/UiFacadeFactoryImpl.java Wed May 30 20:14:55 2012 +0200 +++ b/client/core/src/main/java/com/redhat/thermostat/client/UiFacadeFactoryImpl.java Wed May 30 20:16:19 2012 +0200 @@ -40,8 +40,6 @@ import java.util.Collection; import java.util.concurrent.CountDownLatch; -import org.osgi.framework.BundleContext; - import com.redhat.thermostat.client.osgi.service.VMContextAction; import com.redhat.thermostat.client.osgi.service.VmInformationService; import com.redhat.thermostat.client.ui.HostInformationController; @@ -57,11 +55,17 @@ private Collection<VmInformationService> vmInformationServices = new ArrayList<>(); private Collection<VMContextAction> contextAction = new ArrayList<>(); - + + private MenuRegistry menuRegistry; + + public UiFacadeFactoryImpl(MenuRegistry registry) { + menuRegistry = registry; + } + @Override public MainWindowController getMainWindow() { MainView mainView = new MainWindow(); - return new MainWindowControllerImpl(this, mainView); + return new MainWindowControllerImpl(this, mainView, menuRegistry); } @Override
--- a/client/core/src/main/java/com/redhat/thermostat/client/osgi/ThermostatActivator.java Wed May 30 20:14:55 2012 +0200 +++ b/client/core/src/main/java/com/redhat/thermostat/client/osgi/ThermostatActivator.java Wed May 30 20:16:19 2012 +0200 @@ -43,27 +43,32 @@ import com.redhat.thermostat.client.GUIClientCommand; import com.redhat.thermostat.client.Main; +import com.redhat.thermostat.client.MenuRegistry; import com.redhat.thermostat.client.UiFacadeFactory; import com.redhat.thermostat.client.UiFacadeFactoryImpl; +import com.redhat.thermostat.common.cli.CommandRegistry; import com.redhat.thermostat.common.cli.CommandRegistryImpl; public class ThermostatActivator implements BundleActivator { private VmInformationServiceTracker vmInfoServiceTracker; private VMContextActionServiceTracker contextActionTracker; - + + private CommandRegistry cmdReg; + @Override public void start(final BundleContext context) throws Exception { - UiFacadeFactory uiFacadeFactory = new UiFacadeFactoryImpl(); - + MenuRegistry menuRegistry = new MenuRegistry(context); + UiFacadeFactory uiFacadeFactory = new UiFacadeFactoryImpl(menuRegistry); + vmInfoServiceTracker = new VmInformationServiceTracker(context, uiFacadeFactory); vmInfoServiceTracker.open(); - + contextActionTracker = new VMContextActionServiceTracker(context, uiFacadeFactory); contextActionTracker.open(); - CommandRegistryImpl cmdReg = new CommandRegistryImpl(context); + cmdReg = new CommandRegistryImpl(context); Main main = new Main(uiFacadeFactory, new String[0]); cmdReg.registerCommands(Arrays.asList(new GUIClientCommand(main))); } @@ -72,5 +77,6 @@ public void stop(BundleContext context) throws Exception { vmInfoServiceTracker.close(); //context.removeServiceListener(vmInfoServiceTracker); contextActionTracker.close(); + cmdReg.unregisterCommands(); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/core/src/main/java/com/redhat/thermostat/client/osgi/service/MenuAction.java Wed May 30 20:16:19 2012 +0200 @@ -0,0 +1,55 @@ +/* + * 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.osgi.service; + +/** + * Allows plugins to register menu items. + * <p> + * To register a menu item for for the menu "File" in thermostat client window, + * register a service that implements this class with the property + * "parentMenu" set to "File". + */ +public interface MenuAction { + + /** The string displayed as the menu item name */ + String getName(); + + /** A generic description of the menu item */ + String getDescription(); + + /** Invoked when the user selects this menu item */ + void execute(); +}
--- a/client/core/src/main/java/com/redhat/thermostat/client/osgi/service/VMContextAction.java Wed May 30 20:14:55 2012 +0200 +++ b/client/core/src/main/java/com/redhat/thermostat/client/osgi/service/VMContextAction.java Wed May 30 20:16:19 2012 +0200 @@ -38,7 +38,12 @@ import com.redhat.thermostat.common.dao.VmRef; +/** + * A context action for VMs + */ public interface VMContextAction extends ContextAction { void execute(VmRef referece); + + VMFilter getFilter(); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/core/src/main/java/com/redhat/thermostat/client/osgi/service/VMFilter.java Wed May 30 20:16:19 2012 +0200 @@ -0,0 +1,44 @@ +/* + * 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.osgi.service; + +import com.redhat.thermostat.common.dao.VmRef; + +public interface VMFilter { + + boolean matches(VmRef vm); +}
--- a/client/core/src/main/java/com/redhat/thermostat/client/ui/MainWindow.java Wed May 30 20:14:55 2012 +0200 +++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/MainWindow.java Wed May 30 20:16:19 2012 +0200 @@ -74,6 +74,7 @@ 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; @@ -95,6 +96,7 @@ import com.redhat.thermostat.client.HostsVMsLoader; import com.redhat.thermostat.client.MainView; import com.redhat.thermostat.client.locale.LocaleResources; +import com.redhat.thermostat.client.osgi.service.MenuAction; import com.redhat.thermostat.client.osgi.service.VMContextAction; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.ActionNotifier; @@ -221,6 +223,7 @@ private static final long serialVersionUID = 5608972421496808177L; + private final JMenuBar mainMenuBar; private JPanel contentArea = null; private JTextField searchField = null; @@ -251,6 +254,8 @@ ToolTipManager.sharedInstance().registerComponent(agentVmTree); contentArea = new JPanel(new BorderLayout()); + mainMenuBar = new JMenuBar(); + setupMenus(); setupPanels(); @@ -280,7 +285,6 @@ } private void setupMenus() { - JMenuBar mainMenuBar = new JMenuBar(); JMenu fileMenu = new JMenu(localize(LocaleResources.MENU_FILE)); fileMenu.getPopupMenu().setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY, 1)); @@ -395,7 +399,7 @@ } } }); - setupContextActions(agentVmTree); + registerContextActionListener(agentVmTree); JScrollPane treeScrollPane = new JScrollPane(agentVmTree); @@ -412,14 +416,14 @@ add(splitPane); } - private void setupContextActions(JTree agentVmTree2) { + private void registerContextActionListener(JTree agentVmTree2) { vmContextMenu = new JPopupMenu(); agentVmTree2.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { if (e.isPopupTrigger()) { Ref ref = getSelectedHostOrVm(); if (ref instanceof VmRef) { - vmContextMenu.show((Component)e.getSource(), e.getX(), e.getY()); + fireViewAction(Action.SHOW_VM_CONTEXT_MENU, e); } } } @@ -427,18 +431,30 @@ } @Override - public void registerVMContextAction(final VMContextAction action) { - - JMenuItem contextAction = new JMenuItem(); - contextAction.setText(action.getName()); - contextAction.setToolTipText(action.getDescription()); - vmContextMenu.add(contextAction); - - contextAction.addActionListener(new java.awt.event.ActionListener() { + public void showVMContextActions(final List<VMContextAction> actions, final MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override - public void actionPerformed(ActionEvent e) { - fireViewAction(Action.VM_CONTEXT_ACTION, action); + public void run() { + vmContextMenu.removeAll(); + + for (final VMContextAction action: actions) { + JMenuItem contextAction = new JMenuItem(); + contextAction.setText(action.getName()); + contextAction.setToolTipText(action.getDescription()); + + contextAction.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + fireViewAction(Action.VM_CONTEXT_ACTION, action); + } + }); + vmContextMenu.add(contextAction); + } + + vmContextMenu.show((Component)e.getSource(), e.getX(), e.getY()); } + }); } @@ -561,7 +577,7 @@ actionNotifier.fireAction(action); } - private void fireViewAction(Action action, VMContextAction payload) { + private void fireViewAction(Action action, Object payload) { actionNotifier.fireAction(action, payload); } @@ -623,6 +639,92 @@ }); } + @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 = new JMenuItem(action.getName()); + menu.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + action.execute(); + } + }); + parent.add(menu); + + mainMenuBar.revalidate(); + } + }); + } + + @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(); + } + + 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 */
--- a/client/core/src/test/java/com/redhat/thermostat/client/MainWindowControllerImplTest.java Wed May 30 20:14:55 2012 +0200 +++ b/client/core/src/test/java/com/redhat/thermostat/client/MainWindowControllerImplTest.java Wed May 30 20:16:19 2012 +0200 @@ -37,19 +37,23 @@ package com.redhat.thermostat.client; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.awt.Component; +import java.awt.event.MouseEvent; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.concurrent.TimeUnit; @@ -59,11 +63,12 @@ import org.junit.BeforeClass; import org.junit.Test; import org.mockito.ArgumentCaptor; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; import org.osgi.framework.BundleException; +import com.redhat.thermostat.client.MenuRegistry.MenuListener; +import com.redhat.thermostat.client.osgi.service.MenuAction; import com.redhat.thermostat.client.osgi.service.VMContextAction; +import com.redhat.thermostat.client.osgi.service.VMFilter; import com.redhat.thermostat.client.ui.SummaryController; import com.redhat.thermostat.client.ui.SummaryView; import com.redhat.thermostat.client.ui.VmInformationController; @@ -80,6 +85,7 @@ import com.redhat.thermostat.common.dao.HostRef; import com.redhat.thermostat.common.dao.VmInfoDAO; import com.redhat.thermostat.common.dao.VmRef; +import com.redhat.thermostat.common.model.VmInfo; import com.redhat.thermostat.test.Bug; public class MainWindowControllerImplTest { @@ -99,7 +105,9 @@ private VMContextAction action1; private VMContextAction action2; - + + private MenuListener menuListener; + @BeforeClass public static void setUpOnce() { // TODO remove when controller uses mocked objects rather than real swing objects @@ -129,6 +137,10 @@ ArgumentCaptor<ActionListener> grabListener = ArgumentCaptor.forClass(ActionListener.class); doNothing().when(view).addActionListener(grabListener.capture()); + MenuRegistry registry = mock(MenuRegistry.class); + ArgumentCaptor<MenuListener> menuListenerCaptor = ArgumentCaptor.forClass(MenuListener.class); + doNothing().when(registry).addMenuListener(menuListenerCaptor.capture()); + // TODO remove this asap. the main window has a hard dependency on summary controller/view ViewFactory viewFactory = mock(ViewFactory.class); SummaryView summaryView = mock(SummaryView.class); @@ -136,20 +148,29 @@ ApplicationContext.getInstance().setViewFactory(viewFactory); setUpVMContextActions(); - - controller = new MainWindowControllerImpl(uiFacadeFactory, view); + + controller = new MainWindowControllerImpl(uiFacadeFactory, view, registry); l = grabListener.getValue(); + menuListener = menuListenerCaptor.getValue(); } private void setUpVMContextActions() { action1 = mock(VMContextAction.class); + VMFilter action1Filter = mock(VMFilter.class); + when(action1Filter.matches(isA(VmRef.class))).thenReturn(true); + when(action1.getName()).thenReturn("action1"); when(action1.getDescription()).thenReturn("action1desc"); + when(action1.getFilter()).thenReturn(action1Filter); action2 = mock(VMContextAction.class); + VMFilter action2Filter = mock(VMFilter.class); + when(action2Filter.matches(isA(VmRef.class))).thenReturn(false); + when(action2.getName()).thenReturn("action2"); when(action2.getDescription()).thenReturn("action2desc"); + when(action2.getFilter()).thenReturn(action2Filter); Collection<VMContextAction> actions = new ArrayList<>(); actions.add(action1); @@ -325,12 +346,22 @@ assertEquals(3, id); } - + @Test - public void verityVMActionsAreRegistered() { + public void verityVMActionsAreShown() { + VmInfo vmInfo = new VmInfo(0, 1, 2, null, null, null, null, null, null, null, null, null, null, null); + when(mockVmsDAO.getVmInfo(isA(VmRef.class))).thenReturn(vmInfo); + + VmRef ref = mock(VmRef.class); + when(view.getSelectedHostOrVm()).thenReturn(ref); - verify(view).registerVMContextAction(action1); - verify(view).registerVMContextAction(action2); + MouseEvent uiEvent = mock(MouseEvent.class); + ActionEvent<MainView.Action> viewEvent = new ActionEvent<>(view, MainView.Action.SHOW_VM_CONTEXT_MENU); + viewEvent.setPayload(uiEvent); + + l.actionPerformed(viewEvent); + + verify(view).showVMContextActions(Arrays.asList(action1), uiEvent); } @Test @@ -347,6 +378,19 @@ verify(action2, times(0)).execute(any(VmRef.class)); } + @Test + public void verifyMenuItems() { + assertNotNull(menuListener); + MenuAction action = mock(MenuAction.class); + when(action.getName()).thenReturn("Test1"); + + menuListener.added("File", action); + verify(view).addMenu("File", action); + + menuListener.removed("File", action); + verify(view).removeMenu("File", action); + } + @Test public void testOSGiFrameworkShutdown() throws BundleException {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/core/src/test/java/com/redhat/thermostat/client/MenuRegistryTest.java Wed May 30 20:16:19 2012 +0200 @@ -0,0 +1,90 @@ +/* + * 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; + +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.osgi.framework.BundleContext; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceEvent; +import org.osgi.framework.ServiceListener; +import org.osgi.framework.ServiceReference; + +import com.redhat.thermostat.client.MenuRegistry.MenuListener; +import com.redhat.thermostat.client.osgi.service.MenuAction; + +public class MenuRegistryTest { + + @Test + public void verifyMenuRegistryReactsToMenuActions() throws InvalidSyntaxException { + ArgumentCaptor<ServiceListener> serviceListenerCaptor = ArgumentCaptor.forClass(ServiceListener.class); + ArgumentCaptor<String> filterCaptor = ArgumentCaptor.forClass(String.class); + + MenuListener menuListener = mock(MenuListener.class); + MenuAction menuAction = mock(MenuAction.class); + + BundleContext context = mock(BundleContext.class); + doNothing().when(context).addServiceListener(serviceListenerCaptor.capture(), filterCaptor.capture()); + + 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.start(); + + ServiceListener serviceListener = serviceListenerCaptor.getValue(); + serviceListener.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, ref)); + + verify(menuListener).added(eq("Test"), isA(MenuAction.class)); + + serviceListener.serviceChanged(new ServiceEvent(ServiceEvent.UNREGISTERING, ref)); + + verify(menuListener).removed(eq("Test"), isA(MenuAction.class)); + + } +}
--- a/client/core/src/test/java/com/redhat/thermostat/client/ui/MainWindowTest.java Wed May 30 20:14:55 2012 +0200 +++ b/client/core/src/test/java/com/redhat/thermostat/client/ui/MainWindowTest.java Wed May 30 20:16:19 2012 +0200 @@ -37,15 +37,23 @@ package com.redhat.thermostat.client.ui; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import javax.swing.AbstractAction; + 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.fest.swing.fixture.JTextComponentFixture; @@ -58,6 +66,7 @@ import org.junit.runner.RunWith; import com.redhat.thermostat.client.MainView; +import com.redhat.thermostat.client.osgi.service.MenuAction; import com.redhat.thermostat.common.ActionEvent; import com.redhat.thermostat.common.ActionListener; @@ -191,7 +200,38 @@ verify(l).actionPerformed(new ActionEvent<MainView.Action>(window, MainView.Action.SWITCH_HISTORY_MODE)); } - + + @Category(GUITest.class) + @Test + public void addRemoveMenu() { + final String MENU_NAME = "Test"; + MenuAction action = mock(MenuAction.class); + when(action.getName()).thenReturn(MENU_NAME); + + JMenuItemFixture menuItem; + + frameFixture.show(); + + window.addMenu("File", action); + + menuItem = frameFixture.menuItemWithPath("File", MENU_NAME); + assertNotNull(menuItem); + menuItem.click(); + + verify(action).execute(); + + window.removeMenu("File", action); + + try { + menuItem = frameFixture.menuItemWithPath("File", MENU_NAME); + // should not reach here + assertTrue(false); + } catch (ComponentLookupException cle) { + // expected + } + + } + @Category(GUITest.class) @Test public void testGetHostVMTreeFilter() {
--- a/client/killvm/src/main/java/com/redhat/thermostat/client/killvm/Activator.java Wed May 30 20:14:55 2012 +0200 +++ b/client/killvm/src/main/java/com/redhat/thermostat/client/killvm/Activator.java Wed May 30 20:16:19 2012 +0200 @@ -45,6 +45,7 @@ import org.osgi.framework.ServiceListener; import org.osgi.framework.ServiceReference; +import com.redhat.thermostat.client.osgi.service.ApplicationService; import com.redhat.thermostat.client.osgi.service.ContextAction; import com.redhat.thermostat.client.osgi.service.VMContextAction; import com.redhat.thermostat.service.process.UNIXProcessHandler; @@ -61,7 +62,8 @@ ServiceListener listener = new ServiceListener() { private UNIXProcessHandler unixService; - private boolean[] loaded = new boolean[2]; + private ApplicationService appService; + private boolean[] loaded = new boolean[3]; @Override public void serviceChanged(ServiceEvent event) { @@ -75,6 +77,9 @@ } else if (service instanceof UNIXProcessHandler) { loaded[1] = true; unixService = (UNIXProcessHandler) service; + } else if (service instanceof ApplicationService) { + loaded[2] = true; + appService = (ApplicationService) service; } break; @@ -82,16 +87,17 @@ break; } - if (loaded[0] && loaded[1]) { + if (loaded[0] && loaded[1] && loaded[2]) { context.registerService(VMContextAction.class.getName(), - new KillVMAction(unixService), null); + new KillVMAction(unixService, appService.getDAOFactory()), null); } } }; try { - String filter = "(|(objectClass=" + ContextAction.class.getName() + - ")(objectClass=" + UNIXProcessHandler.class.getName() + "))"; + String filter = "(|(objectClass=" + ContextAction.class.getName() + ")" + + " (objectClass=" + UNIXProcessHandler.class.getName() + ")" + + " (objectClass=" + ApplicationService.class.getName() + "))"; context.addServiceListener(listener, filter); ServiceReference[] services = context.getServiceReferences(null, null);
--- a/client/killvm/src/main/java/com/redhat/thermostat/client/killvm/KillVMAction.java Wed May 30 20:14:55 2012 +0200 +++ b/client/killvm/src/main/java/com/redhat/thermostat/client/killvm/KillVMAction.java Wed May 30 20:16:19 2012 +0200 @@ -37,7 +37,10 @@ package com.redhat.thermostat.client.killvm; import com.redhat.thermostat.client.osgi.service.VMContextAction; +import com.redhat.thermostat.client.osgi.service.VMFilter; +import com.redhat.thermostat.common.dao.DAOFactory; import com.redhat.thermostat.common.dao.VmRef; +import com.redhat.thermostat.common.model.VmInfo; import com.redhat.thermostat.service.process.UNIXProcessHandler; import com.redhat.thermostat.service.process.UNIXSignal; @@ -47,12 +50,14 @@ */ public class KillVMAction implements VMContextAction { - UNIXProcessHandler unixService; - - public KillVMAction(UNIXProcessHandler unixService) { + private final UNIXProcessHandler unixService; + private final DAOFactory dao; + + public KillVMAction(UNIXProcessHandler unixService, DAOFactory dao) { this.unixService = unixService; + this.dao = dao; } - + @Override public String getName() { return "Kill Application"; @@ -67,4 +72,21 @@ public void execute(VmRef reference) { unixService.sendSignal(reference.getIdString(), UNIXSignal.KILL); } + + @Override + public VMFilter getFilter() { + return new LocalAndAliveFilter(); + } + + private class LocalAndAliveFilter implements VMFilter { + + @Override + public boolean matches(VmRef vm) { + // TODO implement local checking too + VmInfo vmInfo = dao.getVmInfoDAO().getVmInfo(vm); + boolean dead = vmInfo.getStartTimeStamp() < vmInfo.getStopTimeStamp(); + return !dead; + } + + } }
--- a/common/src/main/java/com/redhat/thermostat/common/Activator.java Wed May 30 20:14:55 2012 +0200 +++ b/common/src/main/java/com/redhat/thermostat/common/Activator.java Wed May 30 20:16:19 2012 +0200 @@ -36,7 +36,6 @@ package com.redhat.thermostat.common; -import java.util.Collection; import java.util.ServiceLoader; import org.osgi.framework.BundleActivator; @@ -52,14 +51,14 @@ public class Activator implements BundleActivator { - private Collection<ServiceRegistration> cmdRegs; private ServiceRegistration launcherReg; + private CommandRegistry reg; @Override public void start(BundleContext context) throws Exception { - CommandRegistry reg = new CommandRegistryImpl(context); + reg = new CommandRegistryImpl(context); ServiceLoader<Command> cmds = ServiceLoader.load(Command.class, getClass().getClassLoader()); - cmdRegs = reg.registerCommands(cmds); + reg.registerCommands(cmds); CommandContextFactory cmdCtxFactory = new CommandContextFactory(context); Launcher launcher = new LauncherImpl(cmdCtxFactory); @@ -69,9 +68,7 @@ @Override public void stop(BundleContext context) throws Exception { launcherReg.unregister(); - for (ServiceRegistration cmdReg : cmdRegs) { - cmdReg.unregister(); - } + reg.unregisterCommands(); } }
--- a/common/src/main/java/com/redhat/thermostat/common/cli/Command.java Wed May 30 20:14:55 2012 +0200 +++ b/common/src/main/java/com/redhat/thermostat/common/cli/Command.java Wed May 30 20:16:19 2012 +0200 @@ -38,19 +38,50 @@ import java.util.Collection; - +/** + * Represents a command on the command line. + * <p> + * To register a custom command, have a class implement this interface and + * register it as an OSGi service with the {@link #NAME} set to the value of + * {@link #getName()}. + */ public interface Command { - String NAME = "COMMAND_NAME"; + static final String NAME = "COMMAND_NAME"; + /** + * Execute the command + */ void run(CommandContext ctx) throws CommandException; + /** + * Called when the command is being removed from the system. The command + * should cancel any long-term action it has taken, such as any background + * tasks or threads it has spawned. + */ + void disable(); + + /** + * Returns a name for this command. This will be used by the user to select + * this command. + */ String getName(); + /** + * A short description for the command indicating what it does. + */ String getDescription(); + /** + * How the user should invoke this command + */ String getUsage(); + /** + * Returns a collection of arguments that the command is prepared to handle. + * If the user provides unknown or malformed arguments, this command will + * not be invoked. + */ Collection<ArgumentSpec> getAcceptedArguments(); boolean isStorageRequired();
--- a/common/src/main/java/com/redhat/thermostat/common/cli/CommandRegistry.java Wed May 30 20:14:55 2012 +0200 +++ b/common/src/main/java/com/redhat/thermostat/common/cli/CommandRegistry.java Wed May 30 20:16:19 2012 +0200 @@ -44,6 +44,8 @@ public abstract Collection<ServiceRegistration> registerCommands(Iterable<? extends Command> cmds); + public abstract void unregisterCommands(); + public abstract Command getCommand(String name); public abstract Collection<Command> getRegisteredCommands();
--- a/common/src/main/java/com/redhat/thermostat/common/cli/CommandRegistryImpl.java Wed May 30 20:14:55 2012 +0200 +++ b/common/src/main/java/com/redhat/thermostat/common/cli/CommandRegistryImpl.java Wed May 30 20:16:19 2012 +0200 @@ -39,6 +39,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Hashtable; +import java.util.Iterator; import java.util.List; import java.util.logging.Logger; @@ -53,6 +54,8 @@ private BundleContext context; + private List<ServiceRegistration> ourRegistrations = new ArrayList<ServiceRegistration>(); + public CommandRegistryImpl(BundleContext ctx) { context = ctx; } @@ -60,12 +63,27 @@ protected ServiceRegistration registerCommand(Command cmd) { Hashtable<String, String> props = new Hashtable<>(); props.put(Command.NAME, cmd.getName()); - return context.registerService(Command.class.getName(), cmd, props); + ServiceRegistration registration = context.registerService(Command.class.getName(), cmd, props); + ourRegistrations.add(registration); + return registration; + } + + @Override + public void unregisterCommands() { + Iterator<ServiceRegistration> iter = ourRegistrations.iterator(); + while (iter.hasNext()) { + ServiceRegistration registration = iter.next(); + Object serviceObject = context.getService(registration.getReference()); + Command cmd = (Command) serviceObject; + cmd.disable(); + registration.unregister(); + iter.remove(); + } } @Override public Command getCommand(String name) { - ServiceReference[] refs = getCommandServiceRefs("(&(objectclass=*)(COMMAND_NAME=" + name + "))"); + ServiceReference[] refs = getCommandServiceRefs("(&(objectclass=*)(" + Command.NAME + "=" + name + "))"); if (refs == null || refs.length == 0) { return null; } else if (refs.length > 1) {
--- a/common/src/main/java/com/redhat/thermostat/common/cli/HelpCommand.java Wed May 30 20:14:55 2012 +0200 +++ b/common/src/main/java/com/redhat/thermostat/common/cli/HelpCommand.java Wed May 30 20:16:19 2012 +0200 @@ -92,6 +92,9 @@ } @Override + public void disable() { /* NO-OP */ } + + @Override public String getName() { return NAME; }
--- a/common/src/main/java/com/redhat/thermostat/test/TestCommandRegistry.java Wed May 30 20:14:55 2012 +0200 +++ b/common/src/main/java/com/redhat/thermostat/test/TestCommandRegistry.java Wed May 30 20:16:19 2012 +0200 @@ -66,7 +66,7 @@ public void unregister() { // Nothing to do for now. } - + } private Map<String, Command> commands = new HashMap<>(); @@ -83,4 +83,9 @@ commands.put(cmd.getName(), cmd); return new TestServiceRegistration(); } + + @Override + public void unregisterCommands() { + commands.clear(); + } }
--- a/common/src/test/java/com/redhat/thermostat/common/ActivatorTest.java Wed May 30 20:14:55 2012 +0200 +++ b/common/src/test/java/com/redhat/thermostat/common/ActivatorTest.java Wed May 30 20:16:19 2012 +0200 @@ -47,12 +47,16 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Dictionary; +import java.util.HashMap; import java.util.Hashtable; +import java.util.Map; +import java.util.Map.Entry; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; import com.redhat.thermostat.common.cli.Command; @@ -63,17 +67,31 @@ @Test public void testRegisterServices() throws Exception { - final Collection<ServiceRegistration> regs = new ArrayList<>(); + final Map<ServiceRegistration, Object> regs = new HashMap<>(); BundleContext bCtx = mock(BundleContext.class); when(bCtx.registerService(anyString(), any(), any(Dictionary.class))).then(new Answer<ServiceRegistration>() { @Override public ServiceRegistration answer(InvocationOnMock invocation) throws Throwable { ServiceRegistration reg = mock(ServiceRegistration.class); - regs.add(reg); + when(reg.getReference()).thenReturn(mock(ServiceReference.class)); + regs.put(reg, invocation.getArguments()[1]); return reg; } }); + when(bCtx.getService(isA(ServiceReference.class))).then(new Answer<Object>() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + ServiceReference ref = (ServiceReference) invocation.getArguments()[0]; + for (Entry<ServiceRegistration,Object> registration: regs.entrySet()) { + if (registration.getKey().getReference().equals(ref)) { + return registration.getValue(); + } + } + return null; + } + }); + Activator activator = new Activator(); activator.start(bCtx); @@ -86,7 +104,7 @@ activator.stop(bCtx); - for (ServiceRegistration reg : regs) { + for (ServiceRegistration reg : regs.keySet()) { verify(reg).unregister(); } }
--- a/common/src/test/java/com/redhat/thermostat/common/cli/CommandRegistryImplTest.java Wed May 30 20:14:55 2012 +0200 +++ b/common/src/test/java/com/redhat/thermostat/common/cli/CommandRegistryImplTest.java Wed May 30 20:16:19 2012 +0200 @@ -40,6 +40,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -55,6 +56,7 @@ import org.osgi.framework.BundleContext; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; public class CommandRegistryImplTest { @@ -88,6 +90,51 @@ props2.put(Command.NAME, "test2"); verify(bundleContext).registerService(Command.class.getName(), cmd1, props1); verify(bundleContext).registerService(Command.class.getName(), cmd2, props2); + + verifyNoMoreInteractions(bundleContext); + + } + + @Test + public void testUnregisterCommand() { + Command cmd1 = mock(Command.class); + when(cmd1.getName()).thenReturn("test1"); + Command cmd2 = mock(Command.class); + when(cmd2.getName()).thenReturn("test2"); + + ServiceReference cmd1Reference = mock(ServiceReference.class); + ServiceReference cmd2Reference = mock(ServiceReference.class); + + ServiceRegistration cmd1Reg = mock(ServiceRegistration.class); + when(cmd1Reg.getReference()).thenReturn(cmd1Reference); + ServiceRegistration cmd2Reg = mock(ServiceRegistration.class); + when(cmd2Reg.getReference()).thenReturn(cmd2Reference); + + Hashtable<String,String> props1 = new Hashtable<>(); + props1.put(Command.NAME, cmd1.getName()); + Hashtable<String,String> props2 = new Hashtable<>(); + props2.put(Command.NAME, cmd2.getName()); + + when(bundleContext.registerService(Command.class.getName(), cmd1, props1)).thenReturn(cmd1Reg); + when(bundleContext.registerService(Command.class.getName(), cmd2, props2)).thenReturn(cmd2Reg); + + commandRegistry.registerCommands(Arrays.asList(cmd1, cmd2)); + + verify(bundleContext).registerService(Command.class.getName(), cmd1, props1); + verify(bundleContext).registerService(Command.class.getName(), cmd2, props2); + + when(bundleContext.getService(eq(cmd1Reference))).thenReturn(cmd1); + when(bundleContext.getService(eq(cmd2Reference))).thenReturn(cmd2); + + commandRegistry.unregisterCommands(); + + verify(bundleContext).getService(cmd1Reference); + verify(cmd1).disable(); + verify(cmd1Reg).unregister(); + verify(bundleContext).getService(cmd2Reference); + verify(cmd2).disable(); + verify(cmd2Reg).unregister(); + verifyNoMoreInteractions(bundleContext); }
--- a/common/src/test/java/com/redhat/thermostat/common/cli/LauncherTest.java Wed May 30 20:14:55 2012 +0200 +++ b/common/src/test/java/com/redhat/thermostat/common/cli/LauncherTest.java Wed May 30 20:16:19 2012 +0200 @@ -64,6 +64,9 @@ ctx.getConsole().getOutput().print(args.getArgument("arg1") + ", " + args.getArgument("arg2")); } + @Override + public void stop() { /* N0-OP */ } + } private static class TestCmd2 implements TestCommand.Handle { @@ -72,6 +75,11 @@ Arguments args = ctx.getArguments(); ctx.getConsole().getOutput().print(args.getArgument("arg4") + ": " + args.getArgument("arg3")); } + + @Override + public void stop() { + /* NO-OP */ + } } private TestCommandContextFactory ctxFactory; @@ -208,6 +216,9 @@ public void run(CommandContext ctx) throws CommandException { throw new CommandException("test error"); } + + @Override + public void stop() { /* NO-OP */ } }); ctxFactory.getCommandRegistry().registerCommands(Arrays.asList(errorCmd));
--- a/common/src/test/java/com/redhat/thermostat/common/cli/TestCommand.java Wed May 30 20:14:55 2012 +0200 +++ b/common/src/test/java/com/redhat/thermostat/common/cli/TestCommand.java Wed May 30 20:16:19 2012 +0200 @@ -54,6 +54,7 @@ static interface Handle { public void run(CommandContext ctx) throws CommandException; + public void stop(); } TestCommand(String name) { @@ -73,6 +74,13 @@ } @Override + public void disable() { + if (handle != null) { + handle.stop(); + } + } + + @Override public String getName() { return name; }
--- a/common/src/test/java/com/redhat/thermostat/common/tools/BasicCommandTest.java Wed May 30 20:14:55 2012 +0200 +++ b/common/src/test/java/com/redhat/thermostat/common/tools/BasicCommandTest.java Wed May 30 20:16:19 2012 +0200 @@ -64,6 +64,11 @@ } @Override + public void disable() { + // Move along + } + + @Override public String getName() { return null; }
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/Thermostat.java Wed May 30 20:14:55 2012 +0200 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/Thermostat.java Wed May 30 20:16:19 2012 +0200 @@ -40,6 +40,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -54,13 +55,15 @@ import org.osgi.framework.launch.Framework; import org.osgi.framework.launch.FrameworkFactory; - public class Thermostat { /** - * + * the name of the launcher class */ private static final String LAUNCHER_CLASSNAME = "com.redhat.thermostat.common.cli.Launcher"; + + private static final String DEBUG_PREFIX = "OSGi Launcher: "; + private File thermostatBundleHome; private boolean printOSGiDebugInfo = false; @@ -80,7 +83,7 @@ BundleContext bundleContext = framework.getBundleContext(); for (String location : bundleLocations) { if (printOSGiDebugInfo) { - System.out.print("installing bundle: \"" + location + "\""); + System.out.print(DEBUG_PREFIX + "installing bundle: \"" + location + "\""); } Bundle bundle = bundleContext.installBundle(location); if (printOSGiDebugInfo) { @@ -95,7 +98,7 @@ private void startBundles(List<Bundle> bundles) throws BundleException { for (Bundle bundle : bundles) { if (printOSGiDebugInfo) { - System.out.println("starting bundle: \"" + bundle.getBundleId() + "\""); + System.out.println(DEBUG_PREFIX + "starting bundle: \"" + bundle.getBundleId() + "\""); } bundle.start(); } @@ -117,11 +120,27 @@ if (factories.hasNext()) { // we just want the first found - Framework framework = factories.next().newFramework(bundleConfigurations); + final Framework framework = factories.next().newFramework(bundleConfigurations); framework.init(); List<String> bundles = OSGiRegistry.getSystemBundles(); List<Bundle> bundleList = installBundles(framework, bundles); framework.start(); + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + try { + framework.stop(); + framework.waitForStop(0); + if (printOSGiDebugInfo) { + System.out.println(DEBUG_PREFIX + "OSGi framework has shut down"); + } + } catch (Exception e) { + System.err.println("Error stopping framework:" + e); + } + } + }); + startBundles(bundleList); launch(args, framework); @@ -141,6 +160,9 @@ if (launcherRef != null) { Object launcherImpl = ctx.getService(launcherRef); Method m = launcherImpl.getClass().getMethod("run", String[].class); + if (printOSGiDebugInfo) { + System.out.println(DEBUG_PREFIX + "invoking " + launcherImpl.getClass().getName() + "." + m.getName()); + } m.invoke(launcherImpl, (Object) args); } else { System.err.println("Severe: Could not locate launcher"); @@ -158,10 +180,17 @@ public static void main(String[] args) throws Exception { Thermostat t = new Thermostat(); - if (args.length >= 1 && args[0].equals("--print-osgi-info")) { - t.setPrintOSGiDebugInfo(true); + List<String> toProcess = new ArrayList<>(Arrays.asList(args)); + Iterator<String> iter = toProcess.iterator(); + while (iter.hasNext()) { + String arg = iter.next(); + if (("--print-osgi-info").equals(arg)) { + t.setPrintOSGiDebugInfo(true); + iter.remove(); + } } - t.start(args); + + t.start(toProcess.toArray(new String[0])); }
--- a/tools/src/main/java/com/redhat/thermostat/tools/Activator.java Wed May 30 20:14:55 2012 +0200 +++ b/tools/src/main/java/com/redhat/thermostat/tools/Activator.java Wed May 30 20:16:19 2012 +0200 @@ -47,17 +47,18 @@ public class Activator implements BundleActivator { + private CommandRegistry reg; + @Override public void start(BundleContext context) throws Exception { - CommandRegistry reg = new CommandRegistryImpl(context); + reg = new CommandRegistryImpl(context); ServiceLoader<Command> cmds = ServiceLoader.load(Command.class, getClass().getClassLoader()); reg.registerCommands(cmds); } @Override public void stop(BundleContext context) throws Exception { - // TODO Auto-generated method stub - + reg.unregisterCommands(); } }
--- a/tools/src/main/java/com/redhat/thermostat/tools/ThermostatService.java Wed May 30 20:14:55 2012 +0200 +++ b/tools/src/main/java/com/redhat/thermostat/tools/ThermostatService.java Wed May 30 20:16:19 2012 +0200 @@ -38,6 +38,8 @@ import java.util.Arrays; import java.util.Collection; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; @@ -80,6 +82,8 @@ private CommandContext context; + private List<Runnable> tasksOnStop = new CopyOnWriteArrayList<>(); + public ThermostatService() { database = new DBService(); agent = new AgentApplication(); @@ -125,8 +129,8 @@ // we started the database ourselves case START: - // set a shutdown hook if the db was started by us - Runtime.getRuntime().addShutdownHook(new Thread() { + // set a bundle-stop hook if the db was started by us + tasksOnStop.add(new Runnable() { @Override public void run() { String[] args = new String[] { "storage", "--stop" }; @@ -147,6 +151,7 @@ notifier.fireAction(ApplicationState.FAIL); } break; + case FAIL: System.err.println("error starting db"); notifier.fireAction(ApplicationState.FAIL); @@ -163,6 +168,13 @@ } @Override + public void disable() { + for (Runnable task: tasksOnStop) { + task.run(); + } + } + + @Override public String getName() { return NAME; } @@ -183,4 +195,6 @@ ArgumentSpec stop = new SimpleArgumentSpec("stop", "stop the database and agent"); return Arrays.asList(start, stop); } + + }
--- a/tools/src/main/java/com/redhat/thermostat/tools/cli/ListVMsCommand.java Wed May 30 20:14:55 2012 +0200 +++ b/tools/src/main/java/com/redhat/thermostat/tools/cli/ListVMsCommand.java Wed May 30 20:16:19 2012 +0200 @@ -77,6 +77,9 @@ } @Override + public void disable() { /* NO-OP */ } + + @Override public String getName() { return NAME; }
--- a/tools/src/main/java/com/redhat/thermostat/tools/cli/ShellCommand.java Wed May 30 20:14:55 2012 +0200 +++ b/tools/src/main/java/com/redhat/thermostat/tools/cli/ShellCommand.java Wed May 30 20:16:19 2012 +0200 @@ -121,6 +121,9 @@ } @Override + public void disable() { /* NO-OP */ } + + @Override public String getName() { return NAME; }
--- a/tools/src/main/java/com/redhat/thermostat/tools/cli/VMInfoCommand.java Wed May 30 20:14:55 2012 +0200 +++ b/tools/src/main/java/com/redhat/thermostat/tools/cli/VMInfoCommand.java Wed May 30 20:16:19 2012 +0200 @@ -88,6 +88,9 @@ } @Override + public void disable() { /* NO-OP */ } + + @Override public String getName() { return NAME; }
--- a/tools/src/main/java/com/redhat/thermostat/tools/cli/VMStatCommand.java Wed May 30 20:14:55 2012 +0200 +++ b/tools/src/main/java/com/redhat/thermostat/tools/cli/VMStatCommand.java Wed May 30 20:16:19 2012 +0200 @@ -204,4 +204,9 @@ return true; } + @Override + public void disable() { + /* NO-OP */ + } + }
--- a/tools/src/main/java/com/redhat/thermostat/tools/db/DBService.java Wed May 30 20:14:55 2012 +0200 +++ b/tools/src/main/java/com/redhat/thermostat/tools/db/DBService.java Wed May 30 20:16:19 2012 +0200 @@ -183,7 +183,12 @@ throw new InvalidConfigurationException("database directories do not exist..."); } } - + + @Override + public void disable() { + /* NO-OP */ + } + @Override public DBStartupConfiguration getConfiguration() { return configuration;
--- a/tools/src/main/resources/META-INF/services/com.redhat.thermostat.common.cli.Command Wed May 30 20:14:55 2012 +0200 +++ b/tools/src/main/resources/META-INF/services/com.redhat.thermostat.common.cli.Command Wed May 30 20:16:19 2012 +0200 @@ -1,6 +1,5 @@ com.redhat.thermostat.tools.ThermostatService com.redhat.thermostat.tools.db.DBService -#com.redhat.thermostat.agent.AgentApplication com.redhat.thermostat.tools.cli.ListVMsCommand com.redhat.thermostat.tools.cli.ShellCommand com.redhat.thermostat.tools.cli.VMInfoCommand