# HG changeset patch # User Roman Kennke # Date 1358867732 -3600 # Node ID 279aeb153688a04d6a8282020354eb0e205999db # Parent 8a6697503832cbe98eb871be88d1d98a0f464df1# Parent 4f3b798807f3e32e024e3e72072ceee9e01acabb Merge diff -r 8a6697503832 -r 279aeb153688 agent/core/src/main/java/com/redhat/thermostat/backend/Backend.java --- a/agent/core/src/main/java/com/redhat/thermostat/backend/Backend.java Tue Jan 22 16:14:48 2013 +0100 +++ b/agent/core/src/main/java/com/redhat/thermostat/backend/Backend.java Tue Jan 22 16:15:32 2013 +0100 @@ -181,7 +181,8 @@ * This method is called by the framework when the {@link Backend} is * registered. * - * @return true on success, false if there was an error + * @return {@code true} if the backend was activated successfully or + * already active. {@code false} if there was an error */ public abstract boolean activate(); @@ -196,7 +197,8 @@ * This method is called by the framework when the {@link Backend} is * deregistered. * - * @return true on success + * @return {@code true} if the backend was successfully deactivated or + * already inactive. {@code false} if the backend is still active. */ public abstract boolean deactivate(); diff -r 8a6697503832 -r 279aeb153688 client/core/src/main/java/com/redhat/thermostat/client/osgi/service/ContextAction.java --- a/client/core/src/main/java/com/redhat/thermostat/client/osgi/service/ContextAction.java Tue Jan 22 16:14:48 2013 +0100 +++ b/client/core/src/main/java/com/redhat/thermostat/client/osgi/service/ContextAction.java Tue Jan 22 16:15:32 2013 +0100 @@ -1,5 +1,5 @@ /* - * Copyright 2012 Red Hat, Inc. + * Copyright 2013 Red Hat, Inc. * * This file is part of Thermostat. * @@ -36,31 +36,32 @@ package com.redhat.thermostat.client.osgi.service; -import com.redhat.thermostat.annotations.ExtensionPoint; - /** - * Marker service for context menu actions. - *

- * - * Each specific subclass defines the selected entry points for the context - * menus. - *

+ * Parent interface for all context-sensitive actions. + *

+ * {@code ContextAction}s are executed once the user selects the appropriate UI + * elements in the view and triggers the registered action. + *

+ * The name of the action (as returned by {@link #getName()}) is likely to be + * user-visible and should be localized. + * + *

Implementation Notes

+ *

+ * The following information is specific to the current release and may change + * in a future release. + *

+ * The swing client uses {@code ContextAction}s to mostly implement menus. Some + * of these menus are shown when a user right-clicks on a widget, but some are + * associated with a window. * - * Context actions are executed once the user select the appropriate UI elements - * in the main framework view and trigger the registered action. - *

- * - * An empty {@link ContextAction} is instantiated within the framework at - * startup, so services implementing specific actions interfaces should track - * for a {@link ContextAction} service to be active in the framework before - * adding themselves. - *

- * - * Exported entry point: com.redhat.thermostat.client.osgi.service.ContextAction + * @see MenuAction + * @see VMContextAction */ -@ExtensionPoint public interface ContextAction { + /** A user-visible name for this action */ String getName(); + + /** A user-visible description for this action */ String getDescription(); } diff -r 8a6697503832 -r 279aeb153688 client/core/src/main/java/com/redhat/thermostat/client/osgi/service/HostContextAction.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/core/src/main/java/com/redhat/thermostat/client/osgi/service/HostContextAction.java Tue Jan 22 16:15:32 2013 +0100 @@ -0,0 +1,94 @@ +/* + * 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 + * . + * + * 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.annotations.ExtensionPoint; +import com.redhat.thermostat.client.core.Filter; +import com.redhat.thermostat.common.dao.HostRef; + +/** + * {@code HostContextAction}s provide actions that are associated with hosts and + * can be invoked by users. The exact position and appearance of these + * {@code HostContextAction}s varies based on the implementation. + *

+ * Plugins can register implementations of this interface as OSGi services to + * provide additional {@code HostContextAction}s. + *

+ *

Implementation Note

+ *

+ * The following information is specific to the current release and may change + * in a future release. + *

+ * The swing client uses instances of this interface to provide menu items for + * the Host/VM tree. The menu is shown when a user right clicks a host in the + * Host/VM tree. A menu item for every {@code HostContextAction} is added, if + * the {@link Filter} matches, to this menu. Selecting a menu item invokes the + * appropriate {@code HostContextAction}. + * + * @see VMContextAction + */ +@ExtensionPoint +public interface HostContextAction extends ContextAction { + + /** + * A user-visible name for this {@code HostContextAction}. This should be + * localized. + */ + @Override + String getName(); + + /** + * A user-visible description for this {@code HostContextAction}. This + * should be localized. + */ + @Override + String getDescription(); + + /** + * Invoked when the user selects this {@code HostContextAction}. + * + * @param reference the host on which this {@code HostContextAction} was + * invoked on. + */ + void execute(HostRef reference); + + /** + * The {@link Filter} returned by this method is used to select what VMs + * this {@code HostContextAction} is applicable to. + */ + Filter getFilter(); +} diff -r 8a6697503832 -r 279aeb153688 client/core/src/main/java/com/redhat/thermostat/client/osgi/service/MenuAction.java --- a/client/core/src/main/java/com/redhat/thermostat/client/osgi/service/MenuAction.java Tue Jan 22 16:14:48 2013 +0100 +++ b/client/core/src/main/java/com/redhat/thermostat/client/osgi/service/MenuAction.java Tue Jan 22 16:15:32 2013 +0100 @@ -1,5 +1,5 @@ /* - * Copyright 2012 Red Hat, Inc. + * Copyright 2013 Red Hat, Inc. * * This file is part of Thermostat. * @@ -38,11 +38,21 @@ import com.redhat.thermostat.annotations.ExtensionPoint; /** - * Allows plugins to register menu items. + * {@code MenuAction}s are used to create top-level menu items in the main + * window. *

- * 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". + * Plugins can register menu items by creating classes that implement this + * interface and registering them as OSGi services. To register a menu item for + * for the menu "File" in thermostat client window, register a service that + * returns {"File", getName()} from {@link #getPath()}. + * + *

Implementation Notes

+ *

+ * The following information is specific to the current release and may change + * in a future release. + *

+ * The swing client uses {@code MenuActions}s to implement top-level menus in + * the main window only. */ @ExtensionPoint public interface MenuAction extends ContextAction { @@ -51,13 +61,23 @@ CHECK, RADIO, STANDARD - }; + } + + /** The user-visible text displayed as the menu item. */ + @Override + public String getName(); + + /** A user-visible description of what this {@code MenuAction} does. */ + @Override + public String getDescription(); /** Invoked when the user selects this menu item */ void execute(); + /** The type of the menu (radio, check, standard) */ Type getType(); /** The path to the menu action. The last element must equal getName() */ String[] getPath(); + } diff -r 8a6697503832 -r 279aeb153688 client/core/src/main/java/com/redhat/thermostat/client/osgi/service/VMContextAction.java --- a/client/core/src/main/java/com/redhat/thermostat/client/osgi/service/VMContextAction.java Tue Jan 22 16:14:48 2013 +0100 +++ b/client/core/src/main/java/com/redhat/thermostat/client/osgi/service/VMContextAction.java Tue Jan 22 16:15:32 2013 +0100 @@ -1,5 +1,5 @@ /* - * Copyright 2012 Red Hat, Inc. + * Copyright 2013 Red Hat, Inc. * * This file is part of Thermostat. * @@ -41,12 +41,55 @@ import com.redhat.thermostat.common.dao.VmRef; /** - * A context action for VMs + * {@code VMContextAction}s provide actions that are associated with Java + * Virtual Machines and can be invoked by users. The exact position and + * appearance of these {@code VMContextAction}s varies based on the client + * implementation. + *

+ * Plugins can register implementation of this interface as OSGi services to + * provide additional {@code VMContextAction}s. + *

+ *

Implementation Note

+ *

+ * The following information is specific to the current release and may change + * in a future release. + *

+ * The swing client uses instances of this class to provide menu items in the + * Host/VM tree. The menu is shown when a user right-clicks a VM in the Host/VM + * tree. A menu item for every {@link VMContextAction} is added, if the + * {@code Filter} matches, to this menu. Selecting a menu item invokes the + * corresponding {@code VMContextAction}. + * + * @see HostContextAction */ @ExtensionPoint public interface VMContextAction extends ContextAction { - void execute(VmRef referece); + /** + * A user-visible name for this {@code VMContextAction}. Should be + * localized. + */ + @Override + public String getName(); + /** + * A user-visible description for {@code VMContextAction}. Should be + * localized. + */ + @Override + public String getDescription(); + + /** + * Invoked when the user selects this {@code VMContextAction}. + * + * @param reference specifies the vm that this {@code VMContextAction} was + * invoked on. + */ + void execute(VmRef reference); + + /** + * The {@link Filter} returned by this method is used to select what VMs + * this {@code VMContextAction} is applicable to. + */ Filter getFilter(); } diff -r 8a6697503832 -r 279aeb153688 client/core/src/main/java/com/redhat/thermostat/client/ui/UiFacadeFactory.java --- a/client/core/src/main/java/com/redhat/thermostat/client/ui/UiFacadeFactory.java Tue Jan 22 16:14:48 2013 +0100 +++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/UiFacadeFactory.java Tue Jan 22 16:15:32 2013 +0100 @@ -40,6 +40,7 @@ import java.util.List; import com.redhat.thermostat.client.core.InformationService; +import com.redhat.thermostat.client.osgi.service.HostContextAction; import com.redhat.thermostat.client.osgi.service.VMContextAction; import com.redhat.thermostat.common.dao.HostInfoDAO; import com.redhat.thermostat.common.dao.HostRef; @@ -72,6 +73,10 @@ void removeVmInformationService(InformationService vmInfoService); + Collection getHostContextActions(); + void addHostContextAction(HostContextAction action); + void removeHostContextAction(HostContextAction action); + Collection getVMContextActions(); void addVMContextAction(VMContextAction service); diff -r 8a6697503832 -r 279aeb153688 client/swing/src/main/java/com/redhat/thermostat/client/swing/MenuHelper.java --- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/MenuHelper.java Tue Jan 22 16:14:48 2013 +0100 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/MenuHelper.java Tue Jan 22 16:15:32 2013 +0100 @@ -215,8 +215,6 @@ private static final class Menu { private Object swingDelegate; - public Menu() { /* no op */} - public Menu(JMenuItem actual) { this.swingDelegate = actual; } diff -r 8a6697503832 -r 279aeb153688 client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/GUIClientCommand.java --- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/GUIClientCommand.java Tue Jan 22 16:14:48 2013 +0100 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/GUIClientCommand.java Tue Jan 22 16:15:32 2013 +0100 @@ -1,5 +1,5 @@ /* - * Copyright 2012 Red Hat, Inc. + * Copyright 2013 Red Hat, Inc. * * This file is part of Thermostat. * @@ -36,36 +36,22 @@ package com.redhat.thermostat.client.swing.internal; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; - -import com.redhat.thermostat.client.osgi.service.ContextAction; -import com.redhat.thermostat.client.swing.internal.osgi.ContextActionServiceProvider; import com.redhat.thermostat.common.cli.CommandContext; import com.redhat.thermostat.common.cli.CommandException; import com.redhat.thermostat.common.cli.SimpleCommand; public class GUIClientCommand extends SimpleCommand { - private BundleContext context; private Main clientMain; public GUIClientCommand(Main clientMain) { - this(clientMain, FrameworkUtil.getBundle(GUIClientCommand.class).getBundleContext()); - } - - GUIClientCommand(Main clientMain, BundleContext context) { - this.context = context; this.clientMain = clientMain; } - + @Override public void run(CommandContext ctx) throws CommandException { - context.registerService(ContextAction.class.getName(), new ContextActionServiceProvider(), null); - // this blocks, everything else needs to be done before clientMain.run(); - } @Override diff -r 8a6697503832 -r 279aeb153688 client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainView.java --- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainView.java Tue Jan 22 16:14:48 2013 +0100 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainView.java Tue Jan 22 16:15:32 2013 +0100 @@ -43,9 +43,10 @@ import com.redhat.thermostat.client.core.Filter; import com.redhat.thermostat.client.core.views.BasicView; +import com.redhat.thermostat.client.osgi.service.ContextAction; import com.redhat.thermostat.client.osgi.service.DecoratorProvider; import com.redhat.thermostat.client.osgi.service.MenuAction; -import com.redhat.thermostat.client.osgi.service.VMContextAction; +import com.redhat.thermostat.common.ActionEvent; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.HostsVMsLoader; import com.redhat.thermostat.common.dao.HostRef; @@ -64,8 +65,8 @@ SWITCH_HISTORY_MODE, SHOW_ABOUT_DIALOG, SHUTDOWN, - SHOW_VM_CONTEXT_MENU, - VM_CONTEXT_ACTION, + SHOW_HOST_VM_CONTEXT_MENU, + HOST_VM_CONTEXT_ACTION, } void addActionListener(ActionListener capture); @@ -96,11 +97,23 @@ /** * 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. + * non-zero length) and the menu already exists. */ void removeMenu(MenuAction action); - void showVMContextActions(List actions, MouseEvent e); + /** + * Shows a popup context menu created from the list of supplied context + * actions. When an item in the popup menu is selected, an + * {@link ActionEvent} is fired with the id + * {@link Action#HOST_VM_CONTEXT_ACTION} and the user-selected + * {@link ContextAction} as the payload. + * + * @param actions the {@link ContextAction}s available to the user. + * Normally classes implementing sub-interfaces of {@link ContextAction} are used here. + * @param e the mouse event that triggered the context action. Used to + * position the context menu. + */ + void showContextActions(List actions, MouseEvent e); JFrame getTopFrame(); } diff -r 8a6697503832 -r 279aeb153688 client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindow.java --- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindow.java Tue Jan 22 16:14:48 2013 +0100 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindow.java Tue Jan 22 16:15:32 2013 +0100 @@ -93,7 +93,9 @@ import com.redhat.thermostat.client.core.Filter; import com.redhat.thermostat.client.core.views.BasicView; import com.redhat.thermostat.client.locale.LocaleResources; +import com.redhat.thermostat.client.osgi.service.ContextAction; import com.redhat.thermostat.client.osgi.service.DecoratorProvider; +import com.redhat.thermostat.client.osgi.service.HostContextAction; import com.redhat.thermostat.client.osgi.service.MenuAction; import com.redhat.thermostat.client.osgi.service.VMContextAction; import com.redhat.thermostat.client.swing.EdtHelper; @@ -328,7 +330,7 @@ private ActionNotifier actionNotifier = new ActionNotifier<>(this); - private ThermostatPopupMenu vmContextMenu; + private ThermostatPopupMenu contextMenu; private StatusBar statusBar; private final DefaultMutableTreeNode publishedRoot = @@ -505,29 +507,27 @@ } private void registerContextActionListener(JTree agentVmTree2) { - vmContextMenu = new ThermostatPopupMenu(); + contextMenu = new ThermostatPopupMenu(); agentVmTree2.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { if (e.isPopupTrigger()) { Ref ref = getSelectedHostOrVm(); - if (ref instanceof VmRef) { - fireViewAction(Action.SHOW_VM_CONTEXT_MENU, e); - } + fireViewAction(Action.SHOW_HOST_VM_CONTEXT_MENU, e); } } }); } @Override - public void showVMContextActions(final List actions, final MouseEvent e) { + public void showContextActions(final List actions, final MouseEvent e) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - vmContextMenu.removeAll(); + contextMenu.removeAll(); - for (final VMContextAction action: actions) { + for (final ContextAction action: actions) { JMenuItem contextAction = new JMenuItem(); contextAction.setText(action.getName()); contextAction.setToolTipText(action.getDescription()); @@ -535,15 +535,18 @@ contextAction.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent e) { - fireViewAction(Action.VM_CONTEXT_ACTION, action); + fireViewAction(Action.HOST_VM_CONTEXT_ACTION, action); } }); - vmContextMenu.add(contextAction); + + // the component name is for unit tests only + contextAction.setName(action.getName()); + + contextMenu.add(contextAction); } - vmContextMenu.show((Component)e.getSource(), e.getX(), e.getY()); + contextMenu.show((Component)e.getSource(), e.getX(), e.getY()); } - }); } diff -r 8a6697503832 -r 279aeb153688 client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindowControllerImpl.java --- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindowControllerImpl.java Tue Jan 22 16:14:48 2013 +0100 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindowControllerImpl.java Tue Jan 22 16:15:32 2013 +0100 @@ -54,7 +54,9 @@ import com.redhat.thermostat.client.core.views.AgentInformationViewProvider; import com.redhat.thermostat.client.core.views.ClientConfigViewProvider; import com.redhat.thermostat.client.core.views.ClientConfigurationView; +import com.redhat.thermostat.client.osgi.service.ContextAction; import com.redhat.thermostat.client.osgi.service.DecoratorProvider; +import com.redhat.thermostat.client.osgi.service.HostContextAction; import com.redhat.thermostat.client.osgi.service.MenuAction; import com.redhat.thermostat.client.osgi.service.VMContextAction; import com.redhat.thermostat.client.swing.internal.MainView.Action; @@ -295,10 +297,10 @@ case SHOW_ABOUT_DIALOG: showAboutDialog(); break; - case SHOW_VM_CONTEXT_MENU: + case SHOW_HOST_VM_CONTEXT_MENU: showContextMenu(evt); break; - case VM_CONTEXT_ACTION: + case HOST_VM_CONTEXT_ACTION: handleVMHooks(evt); break; case SHUTDOWN: @@ -371,32 +373,49 @@ } private void showContextMenu(ActionEvent evt) { - List toShow = new ArrayList<>(); - VmRef vm = (VmRef) view.getSelectedHostOrVm(); + List toShow = new ArrayList<>(); + + Ref ref = view.getSelectedHostOrVm(); + if (ref instanceof HostRef) { + HostRef vm = (HostRef) ref; + + logger.log(Level.INFO, "registering applicable HostContextActions actions to show"); - logger.log(Level.INFO, "registering applicable VMContextActions actions to show"); + for (HostContextAction action : facadeFactory.getHostContextActions()) { + if (action.getFilter().matches(vm)) { + toShow.add(action); + } + } + } else if (ref instanceof VmRef) { + VmRef vm = (VmRef) ref; - for (VMContextAction action : facadeFactory.getVMContextActions()) { - if (action.getFilter().matches(vm)) { - toShow.add(action); + 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()); + view.showContextActions(toShow, (MouseEvent) evt.getPayload()); } private void handleVMHooks(ActionEvent event) { Object payload = event.getPayload(); - if (payload instanceof VMContextAction) { - try { + try { + if (payload instanceof HostContextAction) { + HostContextAction action = (HostContextAction) payload; + action.execute((HostRef) view.getSelectedHostOrVm()); + } else if (payload instanceof VMContextAction) { VMContextAction action = (VMContextAction) payload; action.execute((VmRef) view.getSelectedHostOrVm()); - } catch (Throwable error) { - logger.log(Level.SEVERE, ""); } + } catch (Throwable error) { + logger.log(Level.SEVERE, "error invocating context action", error); } } - + @Override public void showMainMainWindow() { view.showMainWindow(); diff -r 8a6697503832 -r 279aeb153688 client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/UiFacadeFactoryImpl.java --- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/UiFacadeFactoryImpl.java Tue Jan 22 16:14:48 2013 +0100 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/UiFacadeFactoryImpl.java Tue Jan 22 16:15:32 2013 +0100 @@ -47,6 +47,7 @@ import com.redhat.thermostat.client.core.views.HostInformationViewProvider; import com.redhat.thermostat.client.core.views.SummaryViewProvider; import com.redhat.thermostat.client.core.views.VmInformationViewProvider; +import com.redhat.thermostat.client.osgi.service.HostContextAction; import com.redhat.thermostat.client.osgi.service.VMContextAction; import com.redhat.thermostat.client.ui.HostInformationController; import com.redhat.thermostat.client.ui.MainWindowController; @@ -66,7 +67,8 @@ private List> hostInformationServices = new ArrayList<>(); private List> vmInformationServices = new ArrayList<>(); - private Collection contextAction = new ArrayList<>(); + private Collection hostContextActions = new ArrayList<>(); + private Collection vmContextActions = new ArrayList<>(); private BundleContext context; private ApplicationService appSvc; @@ -136,13 +138,28 @@ } @Override + public Collection getHostContextActions() { + return hostContextActions; + } + + @Override + public void addHostContextAction(HostContextAction action) { + hostContextActions.add(action); + } + + @Override + public void removeHostContextAction(HostContextAction action) { + hostContextActions.remove(action); + } + + @Override public Collection getVMContextActions() { - return contextAction; + return vmContextActions; } @Override public void addVMContextAction(VMContextAction service) { - contextAction.add(service); + vmContextActions.add(service); } @Override diff -r 8a6697503832 -r 279aeb153688 client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/osgi/ContextActionServiceProvider.java --- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/osgi/ContextActionServiceProvider.java Tue Jan 22 16:14:48 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +0,0 @@ -/* - * 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 - * . - * - * 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.swing.internal.osgi; - -import com.redhat.thermostat.client.osgi.service.ContextAction; - -public class ContextActionServiceProvider implements ContextAction { - - @Override - public String getName() { - return "system context"; - } - - @Override - public String getDescription() { - return "system context"; - } -} diff -r 8a6697503832 -r 279aeb153688 client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/osgi/HostContextActionServiceTracker.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/osgi/HostContextActionServiceTracker.java Tue Jan 22 16:15:32 2013 +0100 @@ -0,0 +1,73 @@ +/* + * 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 + * . + * + * 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.swing.internal.osgi; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; + +import com.redhat.thermostat.client.osgi.service.HostContextAction; +import com.redhat.thermostat.client.ui.UiFacadeFactory; + +@SuppressWarnings("rawtypes") +class HostContextActionServiceTracker extends ServiceTracker { + + private UiFacadeFactory uiFacadeFactory; + + private BundleContext context; + + @SuppressWarnings("unchecked") + HostContextActionServiceTracker(BundleContext context, UiFacadeFactory uiFacadeFactory) { + super(context, HostContextAction.class.getName(), null); + this.context = context; + this.uiFacadeFactory = uiFacadeFactory; + } + + @Override + public Object addingService(ServiceReference reference) { + @SuppressWarnings("unchecked") + HostContextAction service = (HostContextAction) super.addingService(reference); + uiFacadeFactory.addHostContextAction(service); + return service; + } + + @Override + public void removedService(ServiceReference reference, Object service) { + uiFacadeFactory.removeHostContextAction((HostContextAction)service); + super.removedService(reference, service); + } +} diff -r 8a6697503832 -r 279aeb153688 client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/osgi/ThermostatActivator.java --- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/osgi/ThermostatActivator.java Tue Jan 22 16:14:48 2013 +0100 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/osgi/ThermostatActivator.java Tue Jan 22 16:15:32 2013 +0100 @@ -70,7 +70,8 @@ public class ThermostatActivator implements BundleActivator { private InformationServiceTracker infoServiceTracker; - private VMContextActionServiceTracker contextActionTracker; + private HostContextActionServiceTracker hostContextActionTracker; + private VMContextActionServiceTracker vmContextActionTracker; private CommandRegistry cmdReg; @@ -111,8 +112,12 @@ infoServiceTracker = new InformationServiceTracker(context, uiFacadeFactory); infoServiceTracker.open(); - contextActionTracker = new VMContextActionServiceTracker(context, uiFacadeFactory); - contextActionTracker.open(); + + hostContextActionTracker = new HostContextActionServiceTracker(context, uiFacadeFactory); + hostContextActionTracker.open(); + + vmContextActionTracker = new VMContextActionServiceTracker(context, uiFacadeFactory); + vmContextActionTracker.open(); cmdReg = new CommandRegistryImpl(context); Main main = new Main(keyring, uiFacadeFactory, new String[0]); @@ -129,7 +134,8 @@ @Override public void stop(BundleContext context) throws Exception { infoServiceTracker.close(); - contextActionTracker.close(); + hostContextActionTracker.close(); + vmContextActionTracker.close(); cmdReg.unregisterCommands(); } } diff -r 8a6697503832 -r 279aeb153688 client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/GUIClientCommandTest.java --- a/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/GUIClientCommandTest.java Tue Jan 22 16:14:48 2013 +0100 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/GUIClientCommandTest.java Tue Jan 22 16:15:32 2013 +0100 @@ -1,5 +1,5 @@ /* - * Copyright 2012 Red Hat, Inc. + * Copyright 2013 Red Hat, Inc. * * This file is part of Thermostat. * @@ -39,23 +39,15 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isNotNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.util.Dictionary; - import org.apache.commons.cli.Options; import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.osgi.framework.BundleContext; -import com.redhat.thermostat.client.osgi.service.ContextAction; -import com.redhat.thermostat.common.ApplicationService; import com.redhat.thermostat.common.cli.CommandContext; import com.redhat.thermostat.common.cli.CommandContextFactory; import com.redhat.thermostat.common.cli.CommandException; @@ -68,8 +60,7 @@ @Before public void setUp() { clientMain = mock(Main.class); - BundleContext ctxt = mock(BundleContext.class); - cmd = new GUIClientCommand(clientMain, ctxt); + cmd = new GUIClientCommand(clientMain); } @After @@ -80,17 +71,15 @@ @Test public void testRun() throws CommandException { - BundleContext bCtx = mock(BundleContext.class); CommandContextFactory cmdCtxFactory = mock(CommandContextFactory.class); CommandContext cmdCtx = mock(CommandContext.class); when(cmdCtx.getCommandContextFactory()).thenReturn(cmdCtxFactory); - cmd = new GUIClientCommand(clientMain, bCtx); + cmd = new GUIClientCommand(clientMain); cmd.run(cmdCtx); verify(clientMain).run(); - verify(bCtx).registerService(eq(ContextAction.class.getName()), isNotNull(), any(Dictionary.class)); } @Test diff -r 8a6697503832 -r 279aeb153688 client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/MainWindowControllerImplTest.java --- a/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/MainWindowControllerImplTest.java Tue Jan 22 16:14:48 2013 +0100 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/MainWindowControllerImplTest.java Tue Jan 22 16:15:32 2013 +0100 @@ -66,7 +66,9 @@ import com.redhat.thermostat.client.core.Filter; import com.redhat.thermostat.client.core.views.BasicView; +import com.redhat.thermostat.client.osgi.service.ContextAction; import com.redhat.thermostat.client.osgi.service.DecoratorProvider; +import com.redhat.thermostat.client.osgi.service.HostContextAction; import com.redhat.thermostat.client.osgi.service.MenuAction; import com.redhat.thermostat.client.osgi.service.VMContextAction; import com.redhat.thermostat.client.ui.SummaryController; @@ -103,8 +105,9 @@ private HostInfoDAO mockHostsDAO; private VmInfoDAO mockVmsDAO; - private VMContextAction action1; - private VMContextAction action2; + private HostContextAction hostContextAction1; + private VMContextAction vmContextAction1; + private VMContextAction vmContextAction2; private HostFilterRegistry hostFilterRegistry; private VmFilterRegistry vmFilterRegistry; @@ -177,6 +180,7 @@ ArgumentCaptor grabInfoRegistry = ArgumentCaptor.forClass(ActionListener.class); doNothing().when(vmInfoRegistry).addActionListener(grabInfoRegistry.capture()); + setUpHostContextActions(); setUpVMContextActions(); controller = new MainWindowControllerImpl(appSvc, uiFacadeFactory, view, registryFactory, mockHostsDAO, mockVmsDAO); @@ -187,26 +191,38 @@ decoratorsListener = grabDecoratorsListener.getValue(); } + private void setUpHostContextActions() { + hostContextAction1 = mock(HostContextAction.class); + Filter hostFilter1 = mock(Filter.class); + when(hostFilter1.matches(isA(HostRef.class))).thenReturn(true); + + when(hostContextAction1.getName()).thenReturn("action1"); + when(hostContextAction1.getDescription()).thenReturn("action1desc"); + when(hostContextAction1.getFilter()).thenReturn(hostFilter1); + + when(uiFacadeFactory.getHostContextActions()).thenReturn(Arrays.asList(hostContextAction1)); + } + private void setUpVMContextActions() { - action1 = mock(VMContextAction.class); + vmContextAction1 = mock(VMContextAction.class); Filter action1Filter = mock(Filter.class); when(action1Filter.matches(isA(VmRef.class))).thenReturn(true); - when(action1.getName()).thenReturn("action1"); - when(action1.getDescription()).thenReturn("action1desc"); - when(action1.getFilter()).thenReturn(action1Filter); + when(vmContextAction1.getName()).thenReturn("action1"); + when(vmContextAction1.getDescription()).thenReturn("action1desc"); + when(vmContextAction1.getFilter()).thenReturn(action1Filter); - action2 = mock(VMContextAction.class); + vmContextAction2 = mock(VMContextAction.class); Filter action2Filter = mock(Filter.class); when(action2Filter.matches(isA(VmRef.class))).thenReturn(false); - when(action2.getName()).thenReturn("action2"); - when(action2.getDescription()).thenReturn("action2desc"); - when(action2.getFilter()).thenReturn(action2Filter); + when(vmContextAction2.getName()).thenReturn("action2"); + when(vmContextAction2.getDescription()).thenReturn("action2desc"); + when(vmContextAction2.getFilter()).thenReturn(action2Filter); Collection actions = new ArrayList<>(); - actions.add(action1); - actions.add(action2); + actions.add(vmContextAction1); + actions.add(vmContextAction2); when(uiFacadeFactory.getVMContextActions()).thenReturn(actions); } @@ -495,7 +511,24 @@ assertEquals(2, id); } - + + @Test + public void verifyHostActionsAreShown() { + HostRef host = mock(HostRef.class); + when(view.getSelectedHostOrVm()).thenReturn(host); + + MouseEvent uiEvent = mock(MouseEvent.class); + ActionEvent viewEvent = new ActionEvent<>(view, MainView.Action.SHOW_HOST_VM_CONTEXT_MENU); + viewEvent.setPayload(uiEvent); + + l.actionPerformed(viewEvent); + + List actions = new ArrayList<>(); + actions.add(hostContextAction1); + + verify(view).showContextActions(actions, uiEvent); + } + @Test public void verityVMActionsAreShown() { VmInfo vmInfo = new VmInfo(0, 1, 2, null, null, null, null, null, null, null, null, null, null, null); @@ -505,26 +538,41 @@ when(view.getSelectedHostOrVm()).thenReturn(ref); MouseEvent uiEvent = mock(MouseEvent.class); - ActionEvent viewEvent = new ActionEvent<>(view, MainView.Action.SHOW_VM_CONTEXT_MENU); + ActionEvent viewEvent = new ActionEvent<>(view, MainView.Action.SHOW_HOST_VM_CONTEXT_MENU); viewEvent.setPayload(uiEvent); l.actionPerformed(viewEvent); - verify(view).showVMContextActions(Arrays.asList(action1), uiEvent); + List actions = new ArrayList<>(); + actions.add(vmContextAction1); + + verify(view).showContextActions(actions, uiEvent); } - + + @Test + public void verifyHostActionsAreExecuted() { + HostRef hostRef = mock(HostRef.class); + when(view.getSelectedHostOrVm()).thenReturn(hostRef); + + ActionEvent event = new ActionEvent<>(view, MainView.Action.HOST_VM_CONTEXT_ACTION); + event.setPayload(hostContextAction1); + l.actionPerformed(event); + + verify(hostContextAction1, times(1)).execute(hostRef); + } + @Test public void verityVMActionsAreExecuted() { VmRef vmRef = mock(VmRef.class); when(view.getSelectedHostOrVm()).thenReturn(vmRef); - ActionEvent event = new ActionEvent<>(view, MainView.Action.VM_CONTEXT_ACTION); - event.setPayload(action1); + ActionEvent event = new ActionEvent<>(view, MainView.Action.HOST_VM_CONTEXT_ACTION); + event.setPayload(vmContextAction1); l.actionPerformed(event); - verify(action1, times(1)).execute(any(VmRef.class)); - verify(action2, times(0)).execute(any(VmRef.class)); + verify(vmContextAction1, times(1)).execute(any(VmRef.class)); + verify(vmContextAction2, times(0)).execute(any(VmRef.class)); } @Test diff -r 8a6697503832 -r 279aeb153688 client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/MainWindowTest.java --- a/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/MainWindowTest.java Tue Jan 22 16:14:48 2013 +0100 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/MainWindowTest.java Tue Jan 22 16:15:32 2013 +0100 @@ -39,6 +39,7 @@ 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.isA; import static org.mockito.Mockito.atLeastOnce; @@ -47,6 +48,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -71,13 +73,14 @@ import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import com.redhat.thermostat.client.core.Filter; +import com.redhat.thermostat.client.osgi.service.ContextAction; import com.redhat.thermostat.client.osgi.service.DecoratorProvider; +import com.redhat.thermostat.client.osgi.service.HostContextAction; import com.redhat.thermostat.client.osgi.service.MenuAction; import com.redhat.thermostat.client.swing.components.SearchField; -import com.redhat.thermostat.client.swing.internal.MainView; -import com.redhat.thermostat.client.swing.internal.MainWindow; import com.redhat.thermostat.client.ui.Decorator; import com.redhat.thermostat.common.ActionEvent; import com.redhat.thermostat.common.ActionListener; @@ -354,4 +357,38 @@ assertEquals(null, window.getSelectedHostOrVm()); } + @GUITest + @Test + public void verifyContextMenu() { + List actions = new ArrayList<>(); + + HostContextAction action = mock(HostContextAction.class); + when(action.getName()).thenReturn("action"); + Filter allMatchingFilter = mock(Filter.class); + when(allMatchingFilter.matches(any(HostRef.class))).thenReturn(true); + + actions.add(action); + + frameFixture.show(); + + // add a second action listener to discard the 'show' event invoked on the first + l = mock(ActionListener.class); + window.addActionListener(l); + + MouseEvent e = new MouseEvent(window, MouseEvent.MOUSE_CLICKED, System.currentTimeMillis(), MouseEvent.BUTTON2_MASK, 0, 0, 0, 0, 1, true, MouseEvent.BUTTON2); + + window.showContextActions(actions, e); + + JMenuItemFixture hostActionMenuItem = frameFixture.menuItem("action"); + hostActionMenuItem.click(); + + ArgumentCaptor actionEventCaptor = ArgumentCaptor.forClass(ActionEvent.class); + verify(l).actionPerformed(actionEventCaptor.capture()); + + ActionEvent actionEvent = actionEventCaptor.getValue(); + assertEquals(window, actionEvent.getSource()); + assertEquals(MainView.Action.HOST_VM_CONTEXT_ACTION, actionEvent.getActionId()); + assertEquals(action, actionEvent.getPayload()); + } + } diff -r 8a6697503832 -r 279aeb153688 client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/osgi/HostContextActionServiceTrackerTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/osgi/HostContextActionServiceTrackerTest.java Tue Jan 22 16:15:32 2013 +0100 @@ -0,0 +1,70 @@ +/* + * 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 + * . + * + * 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.swing.internal.osgi; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import org.junit.Test; +import org.osgi.framework.ServiceRegistration; + +import com.redhat.thermostat.client.osgi.service.HostContextAction; +import com.redhat.thermostat.client.ui.UiFacadeFactory; +import com.redhat.thermostat.test.StubBundleContext; + +public class HostContextActionServiceTrackerTest { + + @Test + public void verifyHostActionIsAddedToAndRemovedFromUiModel() { + StubBundleContext bundleContext = new StubBundleContext(); + UiFacadeFactory applicationModel = mock(UiFacadeFactory.class); + + HostContextAction hostAction = mock(HostContextAction.class); + ServiceRegistration registration = bundleContext.registerService(HostContextAction.class, hostAction, null); + + HostContextActionServiceTracker tracker = new HostContextActionServiceTracker(bundleContext, applicationModel); + tracker.open(); + + registration.unregister(); + + tracker.close(); + + verify(applicationModel).addHostContextAction(hostAction); + verify(applicationModel).removeHostContextAction(hostAction); + } + +} diff -r 8a6697503832 -r 279aeb153688 client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/osgi/ThermostatActivatorTest.java --- a/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/osgi/ThermostatActivatorTest.java Tue Jan 22 16:14:48 2013 +0100 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/osgi/ThermostatActivatorTest.java Tue Jan 22 16:15:32 2013 +0100 @@ -73,5 +73,7 @@ assertTrue(ctx.isServiceRegistered(ClientConfigViewProvider.class.getName(), SwingClientConfigurationViewProvider.class)); assertEquals(6, ctx.getAllServices().size()); + + // FIXME add more tests for the service tracker used by ThermostatActivator } } diff -r 8a6697503832 -r 279aeb153688 common/core/src/main/java/com/redhat/thermostat/common/Pair.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/core/src/main/java/com/redhat/thermostat/common/Pair.java Tue Jan 22 16:15:32 2013 +0100 @@ -0,0 +1,84 @@ +/* + * Copyright 2013 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 + * . + * + * 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.common; + +import java.util.Objects; + +/** + * A container that hold two values. + *

+ * The values may be related or unrelated. + *

+ * For most predictable results, the params should be immutable. If the value of + * {@link #hashCode()} is relevant, the two values must provide sane + * implementations of hashCode too. + * + * @param the type of the first value + * @param the type of the second value + */ +public class Pair { + + private final F first; + private final S second; + + public Pair(F first, S second) { + this.first = first; + this.second = second; + } + + public F getFirst() { + return first; + } + + public S getSecond() { + return second; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Pair)) { + return false; + } + Pair other = (Pair) obj; + return Objects.equals(first, other.first) && Objects.equals(second, other.second); + } + + @Override + public int hashCode() { + return Objects.hash(first, second); + } +} diff -r 8a6697503832 -r 279aeb153688 common/core/src/test/java/com/redhat/thermostat/common/PairTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/core/src/test/java/com/redhat/thermostat/common/PairTest.java Tue Jan 22 16:15:32 2013 +0100 @@ -0,0 +1,78 @@ +/* + * Copyright 2013 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 + * . + * + * 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.common; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class PairTest { + + @Test + public void testCreateAndExtractParts() { + Pair pair = new Pair<>("1", "2"); + + assertEquals("1", pair.getFirst()); + assertEquals("2", pair.getSecond()); + } + + @Test + public void testEquals() { + Pair pair = new Pair<>("1", "2"); + Pair samePair = new Pair<>("1", "2"); + + Pair differentPair = new Pair<>("2", "1"); + + assertFalse(pair.equals(null)); + assertTrue(pair.equals(samePair)); + assertTrue(samePair.equals(pair)); + assertFalse(pair.equals(differentPair)); + assertFalse(pair.equals(new Object())); + } + + @Test + public void testHashCode() { + Pair pair = new Pair<>("1", "2"); + Pair samePair = new Pair<>("1", "2"); + Pair differentPair = new Pair<>("lol", "code"); + + assertFalse(pair.hashCode() == differentPair.hashCode()); + assertTrue(pair.hashCode() == samePair.hashCode()); + } +} diff -r 8a6697503832 -r 279aeb153688 distribution/scripts/thermostat-client-gui-debug --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/distribution/scripts/thermostat-client-gui-debug Tue Jan 22 16:15:32 2013 +0100 @@ -0,0 +1,51 @@ +#!/bin/bash +# +# Copyright 2013 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 +# . +# +# 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. +# +##################################################################### +# +# Some necessary variables. +if [ x"$THERMOSTAT_HOME" = x ] ; then + THERMOSTAT_HOME="@thermostat.home@" +fi +export THERMOSTAT_HOME + +if [ x"$THERMOSTAT_CLIENT_DEBUG_ADDRESS" = x ] ; then + THERMOSTAT_CLIENT_DEBUG_ADDRESS=1080 +fi +export THERMOSTAT_CLIENT_DEBUG_ADDRESS + +$THERMOSTAT_HOME/bin/thermostat gui -J-Dsun.awt.disablegrab=true -J-Xdebug -J-Xrunjdwp:transport=dt_socket,server=y,address=$THERMOSTAT_CLIENT_DEBUG_ADDRESS +exit $? diff -r 8a6697503832 -r 279aeb153688 distribution/scripts/thermostat-client-service-debug --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/distribution/scripts/thermostat-client-service-debug Tue Jan 22 16:15:32 2013 +0100 @@ -0,0 +1,51 @@ +#!/bin/bash +# +# Copyright 2013 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 +# . +# +# 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. +# +##################################################################### +# +# Some necessary variables. +if [ x"$THERMOSTAT_HOME" = x ] ; then + THERMOSTAT_HOME="@thermostat.home@" +fi +export THERMOSTAT_HOME + +if [ x"$THERMOSTAT_CLIENT_DEBUG_ADDRESS" = x ] ; then + THERMOSTAT_SERVICE_DEBUG_ADDRESS=1081 +fi +export THERMOSTAT_SERVICE_DEBUG_ADDRESS + +$THERMOSTAT_HOME/bin/thermostat service -J-Xdebug -J-Xrunjdwp:transport=dt_socket,server=y,address=$THERMOSTAT_SERVICE_DEBUG_ADDRESS +exit $? diff -r 8a6697503832 -r 279aeb153688 killvm/client-swing/src/main/java/com/redhat/thermostat/killvm/client/internal/Activator.java --- a/killvm/client-swing/src/main/java/com/redhat/thermostat/killvm/client/internal/Activator.java Tue Jan 22 16:14:48 2013 +0100 +++ b/killvm/client-swing/src/main/java/com/redhat/thermostat/killvm/client/internal/Activator.java Tue Jan 22 16:15:32 2013 +0100 @@ -42,7 +42,6 @@ import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; -import com.redhat.thermostat.client.osgi.service.ContextAction; import com.redhat.thermostat.client.osgi.service.VMContextAction; import com.redhat.thermostat.common.MultipleServiceTracker; import com.redhat.thermostat.common.MultipleServiceTracker.Action; @@ -59,7 +58,6 @@ Class[] serviceDeps = new Class[] { AgentInfoDAO.class, VmInfoDAO.class, - ContextAction.class, }; killVmActionTracker = new MultipleServiceTracker(context, serviceDeps, new Action() { diff -r 8a6697503832 -r 279aeb153688 storage/core/src/main/java/com/redhat/thermostat/storage/core/Query.java --- a/storage/core/src/main/java/com/redhat/thermostat/storage/core/Query.java Tue Jan 22 16:14:48 2013 +0100 +++ b/storage/core/src/main/java/com/redhat/thermostat/storage/core/Query.java Tue Jan 22 16:15:32 2013 +0100 @@ -51,7 +51,7 @@ LESS_THAN, LESS_THAN_OR_EQUAL_TO, } - + enum SortDirection { ASCENDING(1), DESCENDING(-1); @@ -68,7 +68,7 @@ } void where(Key key, Criteria criteria, S value); - + void sort(Key key, SortDirection direction); void limit(int n); diff -r 8a6697503832 -r 279aeb153688 storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoQuery.java --- a/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoQuery.java Tue Jan 22 16:14:48 2013 +0100 +++ b/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoQuery.java Tue Jan 22 16:15:32 2013 +0100 @@ -36,9 +36,13 @@ package com.redhat.thermostat.storage.mongodb.internal; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Objects; import com.mongodb.BasicDBObject; +import com.mongodb.BasicDBObjectBuilder; import com.mongodb.DBObject; import com.redhat.thermostat.storage.core.AbstractQuery; import com.redhat.thermostat.storage.core.Category; @@ -50,6 +54,8 @@ private MongoStorage storage; private BasicDBObject query = new BasicDBObject(); + private Map builerMap; + private boolean hasClauses = false; private Category category; private Class resultClass; @@ -58,6 +64,7 @@ this.storage = storage; this.category = category; this.resultClass = category.getDataClass(); + this.builerMap = new HashMap(); } public Category getCategory() { @@ -74,31 +81,45 @@ } public void where(String key, Criteria operator, Object value) { - switch (operator) { - case EQUALS: + + // strict equality is mutually exclusive on the key + if (operator.equals(Criteria.EQUALS)) { query.put(key, value); - break; - - case NOT_EQUAL_TO: - query.put(key, new BasicDBObject("$ne", value)); - break; - - case LESS_THAN: - query.put(key, new BasicDBObject("$lt", value)); - break; - - case LESS_THAN_OR_EQUAL_TO: - query.put(key, new BasicDBObject("$lte", value)); - break; - case GREATER_THAN: - query.put(key, new BasicDBObject("$gt", value)); - break; - - case GREATER_THAN_OR_EQUAL_TO: - query.put(key, new BasicDBObject("$gte", value)); - break; - default: - throw new IllegalArgumentException("MongoQuery can not handle " + operator); + + } else { + BasicDBObjectBuilder queryParameters = null; + if (builerMap.containsKey(key)) { + queryParameters = (BasicDBObjectBuilder) builerMap.get(key); + } else { + queryParameters = BasicDBObjectBuilder.start(); + builerMap.put(key, queryParameters); + } + + switch (operator) { + + case NOT_EQUAL_TO: + queryParameters.add("$ne", value); + break; + + case LESS_THAN: + queryParameters.add("$lt", value); + break; + + case LESS_THAN_OR_EQUAL_TO: + queryParameters.add("$lte", value); + break; + case GREATER_THAN: + queryParameters.add("$gt", value); + break; + + case GREATER_THAN_OR_EQUAL_TO: + queryParameters.add("$gte", value); + break; + + default: + throw new IllegalArgumentException("MongoQuery can not handle " + operator); + } + query.put(key, queryParameters.get()); } hasClauses = true; } diff -r 8a6697503832 -r 279aeb153688 storage/mongo/src/test/java/com/redhat/thermostat/storage/mongodb/internal/MongoQueryTest.java --- a/storage/mongo/src/test/java/com/redhat/thermostat/storage/mongodb/internal/MongoQueryTest.java Tue Jan 22 16:14:48 2013 +0100 +++ b/storage/mongo/src/test/java/com/redhat/thermostat/storage/mongodb/internal/MongoQueryTest.java Tue Jan 22 16:15:32 2013 +0100 @@ -45,6 +45,7 @@ import org.junit.Test; import com.mongodb.BasicDBObject; +import com.mongodb.BasicDBObjectBuilder; import com.mongodb.DBObject; import com.redhat.thermostat.storage.core.Category; import com.redhat.thermostat.storage.core.Query.Criteria; @@ -121,6 +122,48 @@ assertEquals(new BasicDBObject("$lte", "value"), generatedQuery.get("key")); } + @Test + public void testMultiWhere() { + MongoQuery query = new MongoQuery<>(storage, category); + query.where("test", Criteria.LESS_THAN_OR_EQUAL_TO, 1); + query.where("test", Criteria.GREATER_THAN, 2); + + DBObject generatedQuery = query.getGeneratedQuery(); + DBObject dbObject = BasicDBObjectBuilder.start("$lte", 1).add("$gt", 2).get(); + assertEquals(dbObject, generatedQuery.get("test")); + } + + @Test + public void testMultiWhere2() { + MongoQuery query = new MongoQuery<>(storage, category); + query.where("test", Criteria.LESS_THAN_OR_EQUAL_TO, 1); + query.where("test2", Criteria.GREATER_THAN, 2); + + DBObject generatedQuery = query.getGeneratedQuery(); + assertEquals(new BasicDBObject("$lte", 1), generatedQuery.get("test")); + } + + @Test + public void testMultiWhere3() { + MongoQuery query = new MongoQuery<>(storage, category); + query.where("test", Criteria.EQUALS, 1); + query.where("test", Criteria.GREATER_THAN, 2); + + DBObject generatedQuery = query.getGeneratedQuery(); + assertEquals(new BasicDBObject("$gt", 2), generatedQuery.get("test")); + } + + @Test + public void testMultiWhere4() { + MongoQuery query = new MongoQuery<>(storage, category); + query.where("test", Criteria.EQUALS, 1); + query.where("test2", Criteria.GREATER_THAN, 2); + + DBObject generatedQuery = query.getGeneratedQuery(); + assertEquals(1, generatedQuery.get("test")); + assertEquals(new BasicDBObject("$gt", 2), generatedQuery.get("test2")); + } + private DBObject generateSimpleWhereQuery(String key, Criteria criteria, Object value) { MongoQuery query = new MongoQuery<>(storage, category); query.where(key, criteria, value); diff -r 8a6697503832 -r 279aeb153688 thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTimelineView.java --- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTimelineView.java Tue Jan 22 16:14:48 2013 +0100 +++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTimelineView.java Tue Jan 22 16:15:32 2013 +0100 @@ -38,42 +38,29 @@ import java.awt.BorderLayout; import java.awt.Component; -import java.awt.Dimension; -import java.awt.GridLayout; -import java.awt.Point; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; + import java.util.List; -import java.util.Map; import javax.swing.DefaultListModel; import javax.swing.JList; import javax.swing.JPanel; +import javax.swing.JScrollBar; import javax.swing.JScrollPane; -import javax.swing.ListCellRenderer; import javax.swing.SwingUtilities; -import javax.swing.SwingWorker; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import com.redhat.thermostat.client.swing.ComponentVisibleListener; import com.redhat.thermostat.client.swing.SwingComponent; import com.redhat.thermostat.common.model.LongRange; - import com.redhat.thermostat.thread.client.common.Timeline; -import com.redhat.thermostat.thread.client.common.TimelineInfo; import com.redhat.thermostat.thread.client.common.view.ThreadTimelineView; import com.redhat.thermostat.thread.client.swing.impl.timeline.TimelineCellRenderer; import com.redhat.thermostat.thread.client.swing.impl.timeline.TimelineComponent; import com.redhat.thermostat.thread.client.swing.impl.timeline.TimelineRulerHeader; import com.redhat.thermostat.thread.client.swing.impl.timeline.TimelineUtils; -import com.redhat.thermostat.thread.model.ThreadInfoData; public class SwingThreadTimelineView extends ThreadTimelineView implements SwingComponent { - - private final String lock = new String("SwingThreadTimelineViewLock"); private JPanel timeLinePanel; private JList chartList; @@ -110,6 +97,14 @@ timeLinePanel.add(timelineLegend, BorderLayout.SOUTH); } + private class ScrollChangeListener implements ChangeListener { + @Override + public void stateChanged(ChangeEvent e) { + scrollPane.repaint(); + header.repaint(); + } + } + private JScrollPane createScrollPane() { scrollPane = new JScrollPane(chartList); scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); @@ -118,52 +113,52 @@ long now = System.currentTimeMillis(); header = new TimelineRulerHeader(new LongRange(now, now + TimelineUtils.STEP), scrollPane); scrollPane.setColumnHeaderView(header); - scrollPane.getHorizontalScrollBar().getModel().addChangeListener(new ChangeListener() { + + ScrollChangeListener listener = new ScrollChangeListener(); + + scrollPane.getHorizontalScrollBar().getModel().addChangeListener(listener); + scrollPane.getVerticalScrollBar().getModel().addChangeListener(listener); + + return scrollPane; + } + + private void handleScrollBar() { + SwingUtilities.invokeLater(new Runnable() { @Override - public void stateChanged(ChangeEvent e) { - scrollPane.repaint(); + public void run() { + JScrollBar scrollBar = scrollPane.getHorizontalScrollBar(); + if (!chartModel.isEmpty()) { + TimelineComponent component = chartModel.getElementAt(0); + + int extent = scrollBar.getVisibleAmount(); + int min = scrollBar.getMinimum(); + int max = component.getWidth() + (2 * TimelineUtils.INC); + + scrollBar.setValues(max - extent, extent, min, max); + } } }); - scrollPane.getVerticalScrollBar().getModel().addChangeListener(new ChangeListener() { - @Override - public void stateChanged(ChangeEvent e) { - scrollPane.repaint(); - } - }); - return scrollPane; } @Override public void displayStats(final List timelines, final LongRange range) { + SwingUtilities.invokeLater(new Runnable() { @Override public void run() { + range.setMax(range.getMax() + (2 * TimelineUtils.STEP)); chartModel.removeAllElements(); for (Timeline timeline : timelines) { chartModel.addElement(new TimelineComponent(range, timeline, scrollPane)); } header.getRange().setMin(range.getMin()); header.getRange().setMax(range.getMax()); + + handleScrollBar(); } }); } -// private class SelectedThreadListener implements PropertyChangeListener { -// @Override -// public void propertyChange(final PropertyChangeEvent evt) { -// SwingWorker notifier = new SwingWorker() { -// @Override -// protected Void doInBackground() throws Exception { -// SwingThreadTimelineView.this. -// threadTimelineNotifier.fireAction(ThreadTimelineView.ThreadTimelineViewAction.THREAD_TIMELINE_SELECTED, -// evt.getNewValue()); -// return null; -// } -// }; -// notifier.execute(); -// } -// } - @Override public Component getUiComponent() { return timeLinePanel; diff -r 8a6697503832 -r 279aeb153688 vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/Activator.java --- a/vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/Activator.java Tue Jan 22 16:14:48 2013 +0100 +++ b/vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/Activator.java Tue Jan 22 16:15:32 2013 +0100 @@ -42,6 +42,7 @@ import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; +import com.redhat.thermostat.agent.VmStatusListenerRegistrar; import com.redhat.thermostat.backend.Backend; import com.redhat.thermostat.backend.BackendService; import com.redhat.thermostat.common.MultipleServiceTracker; @@ -57,6 +58,9 @@ @Override public void start(final BundleContext context) throws Exception { + + final VmStatusListenerRegistrar registrar = new VmStatusListenerRegistrar(context); + Class[] deps = new Class[] { BackendService.class, VmClassStatDAO.class @@ -67,7 +71,7 @@ public void dependenciesAvailable(Map services) { VmClassStatDAO vmClassStatDao = (VmClassStatDAO) services.get(VmClassStatDAO.class.getName()); Version version = new Version(context.getBundle()); - backend = new VmClassStatBackend(vmClassStatDao, version); + backend = new VmClassStatBackend(vmClassStatDao, version, registrar); reg = context.registerService(Backend.class.getName(), backend, null); } diff -r 8a6697503832 -r 279aeb153688 vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackend.java --- a/vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackend.java Tue Jan 22 16:14:48 2013 +0100 +++ b/vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackend.java Tue Jan 22 16:15:32 2013 +0100 @@ -37,42 +37,53 @@ package com.redhat.thermostat.vm.classstat.agent.internal; import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import sun.jvmstat.monitor.HostIdentifier; import sun.jvmstat.monitor.MonitorException; import sun.jvmstat.monitor.MonitoredHost; +import sun.jvmstat.monitor.MonitoredVm; +import sun.jvmstat.monitor.VmIdentifier; +import sun.jvmstat.monitor.event.VmListener; +import com.redhat.thermostat.agent.VmStatusListener; +import com.redhat.thermostat.agent.VmStatusListenerRegistrar; import com.redhat.thermostat.backend.Backend; import com.redhat.thermostat.backend.BackendID; import com.redhat.thermostat.backend.BackendsProperties; +import com.redhat.thermostat.common.Pair; import com.redhat.thermostat.common.Version; import com.redhat.thermostat.common.utils.LoggingUtils; import com.redhat.thermostat.vm.classstat.common.VmClassStatDAO; -public class VmClassStatBackend extends Backend { +public class VmClassStatBackend extends Backend implements VmStatusListener { private static final Logger LOGGER = LoggingUtils.getLogger(VmClassStatBackend.class); - private VmClassStatDAO vmClassStats; - private HostIdentifier hostId; + private final VmClassStatDAO vmClassStats; + private final VmStatusListenerRegistrar registrar; + + private final Map> pidToVmAndListener = new HashMap<>(); + private MonitoredHost host; - private VmClassStatHostListener hostListener; + private boolean started; - public VmClassStatBackend(VmClassStatDAO vmClassStatDAO, Version version) { + public VmClassStatBackend(VmClassStatDAO vmClassStatDAO, Version version, VmStatusListenerRegistrar registrar) { super(new BackendID("VM Classes Backend", VmClassStatBackend.class.getName())); this.vmClassStats = vmClassStatDAO; + this.registrar = registrar; setConfigurationValue(BackendsProperties.VENDOR.name(), "Red Hat, Inc."); setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers class loading statistics about a JVM"); setConfigurationValue(BackendsProperties.VERSION.name(), version.getVersionNumber()); try { - hostId = new HostIdentifier((String) null); + HostIdentifier hostId = new HostIdentifier((String) null); host = MonitoredHost.getMonitoredHost(hostId); - hostListener = new VmClassStatHostListener(vmClassStats, attachToNewProcessByDefault()); } catch (MonitorException me) { LOGGER.log(Level.WARNING, "Problems with connecting jvmstat to local machine", me); } catch (URISyntaxException use) { @@ -80,29 +91,24 @@ } } + /* + * Methods from Backend + */ + @Override public boolean activate() { if (!started && host != null) { - try { - host.addHostListener(hostListener); - started = true; - } catch (MonitorException me) { - LOGGER.log(Level.WARNING, "Failed to add host listener", me); - } - + registrar.register(this); + started = true; } return started; } @Override public boolean deactivate() { - if (started && host != null) { - try { - host.removeHostListener(hostListener); - started = false; - } catch (MonitorException me) { - LOGGER.log(Level.INFO, "Failed to remove host listener"); - } + if (started) { + registrar.unregister(this); + started = false; } return !started; } @@ -128,10 +134,69 @@ } /* + * Methods from VmStatusListener + */ + + @Override + public void vmStatusChanged(Status newStatus, int pid) { + switch (newStatus) { + case VM_STARTED: + /* fall-through */ + case VM_ACTIVE: + vmStarted(pid); + break; + case VM_STOPPED: + vmStopped(pid); + break; + } + } + + private void vmStarted(int pid) { + if (attachToNewProcessByDefault()) { + try { + MonitoredVm vm = host.getMonitoredVm(host.getHostIdentifier().resolve(new VmIdentifier(String.valueOf(pid)))); + VmClassStatVmListener listener = new VmClassStatVmListener(vmClassStats, pid); + vm.addVmListener(listener); + + pidToVmAndListener.put(pid, new Pair<>(vm, listener)); + LOGGER.finer("Attached VmListener for VM: " + pid); + } catch (MonitorException | URISyntaxException e) { + LOGGER.log(Level.WARNING, "Could not attach to new vm " + pid, e); + } + } else { + LOGGER.log(Level.FINE, "skipping new vm " + pid); + } + } + + private void vmStopped(Integer pid) { + Pair data = pidToVmAndListener.remove(pid); + // if there is no data, we must never have attached to the vm. Nothing to do. + if (data == null) { + return; + } + + MonitoredVm vm = data.getFirst(); + VmListener listener = data.getSecond(); + try { + vm.removeVmListener(listener); + } catch (MonitorException e) { + LOGGER.log(Level.WARNING, "can't remove vm listener", e); + } + vm.detach(); + } + + /* * For testing purposes only. */ void setHost(MonitoredHost host) { this.host = host; } - + + /* + * For testing purposes only. + */ + Map> getPidToDataMap() { + return pidToVmAndListener; + } + } diff -r 8a6697503832 -r 279aeb153688 vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatHostListener.java --- a/vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatHostListener.java Tue Jan 22 16:14:48 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,170 +0,0 @@ -/* - * Copyright 2013 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 - * . - * - * 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.vm.classstat.agent.internal; - -import java.net.URISyntaxException; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; - -import sun.jvmstat.monitor.MonitorException; -import sun.jvmstat.monitor.MonitoredHost; -import sun.jvmstat.monitor.MonitoredVm; -import sun.jvmstat.monitor.VmIdentifier; -import sun.jvmstat.monitor.event.HostEvent; -import sun.jvmstat.monitor.event.HostListener; -import sun.jvmstat.monitor.event.VmListener; -import sun.jvmstat.monitor.event.VmStatusChangeEvent; - -import com.redhat.thermostat.common.utils.LoggingUtils; -import com.redhat.thermostat.vm.classstat.common.VmClassStatDAO; - -public class VmClassStatHostListener implements HostListener { - - private static final Logger logger = LoggingUtils.getLogger(VmClassStatHostListener.class); - - private boolean attachNew; - - private final VmClassStatDAO vmClassStatDAO; - - private Map monitoredVms = new HashMap<>(); - private Map registeredListeners = new ConcurrentHashMap<>(); - - VmClassStatHostListener(VmClassStatDAO vmClassStatDAO, boolean attachNew) { - this.vmClassStatDAO = vmClassStatDAO; - this.attachNew = attachNew; - } - - void removeAllListeners() { - for (MonitoredVm vm : monitoredVms.values()) { - VmListener listener = registeredListeners.get(vm); - try { - if (listener != null) { - vm.removeVmListener(listener); - } - } catch (MonitorException e) { - logger.log(Level.WARNING, "can't remove vm listener", e); - } - } - } - - @Override - public void disconnected(HostEvent event) { - logger.warning("Disconnected from host"); - } - - @SuppressWarnings("unchecked") // Unchecked casts to (Set). - @Override - public void vmStatusChanged(VmStatusChangeEvent event) { - MonitoredHost host = event.getMonitoredHost(); - - for (Integer newVm : (Set) event.getStarted()) { - try { - logger.fine("New vm: " + newVm); - sendNewVM(newVm, host); - } catch (MonitorException e) { - logger.log(Level.WARNING, "error getting info for new vm" + newVm, e); - } catch (URISyntaxException e) { - logger.log(Level.WARNING, "error getting info for new vm" + newVm, e); - } - } - - for (Integer stoppedVm : (Set) event.getTerminated()) { - try { - logger.fine("stopped vm: " + stoppedVm); - sendStoppedVM(stoppedVm, host); - } catch (URISyntaxException e) { - logger.log(Level.WARNING, "error getting info for stopped vm" + stoppedVm, e); - } catch (MonitorException e) { - logger.log(Level.WARNING, "error getting info for stopped vm" + stoppedVm, e); - } - } - } - - private void sendNewVM(Integer vmId, MonitoredHost host) - throws MonitorException, URISyntaxException { - MonitoredVm vm = host.getMonitoredVm(host.getHostIdentifier().resolve( - new VmIdentifier(vmId.toString()))); - if (vm != null) { - if (attachNew) { - VmClassStatVmListener listener = new VmClassStatVmListener(vmClassStatDAO, vmId); - vm.addVmListener(listener); - - registeredListeners.put(vm, listener); - logger.finer("Attached VmListener for VM: " + vmId); - } else { - logger.log(Level.FINE, "skipping new vm " + vmId); - } - - monitoredVms.put(vmId, vm); - } - } - - private void sendStoppedVM(Integer vmId, MonitoredHost host) throws URISyntaxException, MonitorException { - - VmIdentifier resolvedVmID = host.getHostIdentifier().resolve(new VmIdentifier(vmId.toString())); - if (resolvedVmID != null) { - MonitoredVm vm = monitoredVms.remove(vmId); - VmListener listener = registeredListeners.remove(vm); - try { - if (listener != null) { - vm.removeVmListener(listener); - } - } catch (MonitorException e) { - logger.log(Level.WARNING, "can't remove vm listener", e); - } - vm.detach(); - } - } - - /* - * For testing purposes only. - */ - Map getMonitoredVms() { - return monitoredVms; - } - - /* - * For testing purposes only. - */ - Map getRegisteredListeners() { - return registeredListeners; - } -} diff -r 8a6697503832 -r 279aeb153688 vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/ActivatorTest.java --- a/vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/ActivatorTest.java Tue Jan 22 16:14:48 2013 +0100 +++ b/vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/ActivatorTest.java Tue Jan 22 16:15:32 2013 +0100 @@ -92,6 +92,9 @@ VmClassStatBackend backend = activator.getBackend(); assertNotNull(backend); + // something in core thermostat activates the backend; do it manually here + backend.activate(); + activator.stop(context); assertFalse(backend.isActive()); diff -r 8a6697503832 -r 279aeb153688 vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackendTest.java --- a/vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackendTest.java Tue Jan 22 16:14:48 2013 +0100 +++ b/vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackendTest.java Tue Jan 22 16:15:32 2013 +0100 @@ -38,20 +38,30 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import java.net.URISyntaxException; import org.junit.Before; import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import sun.jvmstat.monitor.HostIdentifier; import sun.jvmstat.monitor.MonitorException; import sun.jvmstat.monitor.MonitoredHost; -import sun.jvmstat.monitor.event.HostListener; +import sun.jvmstat.monitor.MonitoredVm; +import sun.jvmstat.monitor.VmIdentifier; +import com.redhat.thermostat.agent.VmStatusListenerRegistrar; +import com.redhat.thermostat.agent.VmStatusListener.Status; +import com.redhat.thermostat.common.Ordered; import com.redhat.thermostat.common.Version; import com.redhat.thermostat.vm.classstat.common.VmClassStatDAO; @@ -59,6 +69,9 @@ private VmClassStatBackend backend; private MonitoredHost host; + private VmStatusListenerRegistrar registrar; + private HostIdentifier hostIdentifier; + private MonitoredVm monitoredVm1; @Before public void setup() throws MonitorException, URISyntaxException { @@ -67,25 +80,139 @@ Version version = mock(Version.class); when(version.getVersionNumber()).thenReturn("0.0.0"); - backend = new VmClassStatBackend(vmClassStatDao, version); + registrar = mock(VmStatusListenerRegistrar.class); + + hostIdentifier = mock(HostIdentifier.class); + when(hostIdentifier.resolve(isA(VmIdentifier.class))).then(new Answer() { + @Override + public VmIdentifier answer(InvocationOnMock invocation) throws Throwable { + return (VmIdentifier) invocation.getArguments()[0]; + } + }); + host = mock(MonitoredHost.class); + when(host.getHostIdentifier()).thenReturn(hostIdentifier); + + monitoredVm1 = mock(MonitoredVm.class); + + backend = new VmClassStatBackend(vmClassStatDao, version, registrar); - host = mock(MonitoredHost.class); backend.setHost(host); } @Test - public void testStart() throws MonitorException { + public void testActivate() { backend.activate(); - verify(host).addHostListener(any(HostListener.class)); + assertTrue(backend.isActive()); + verify(registrar).register(backend); + } + + @Test + public void testActivateTwice() { + assertTrue(backend.activate()); + assertTrue(backend.isActive()); + + assertTrue(backend.activate()); assertTrue(backend.isActive()); } @Test - public void testStop() throws MonitorException { - backend.activate(); - backend.deactivate(); - verify(host).removeHostListener(any(HostListener.class)); + public void testCanNotActivateWithoutMonitoredHost() { + backend.setHost(null); + + assertFalse(backend.activate()); assertFalse(backend.isActive()); } + @Test + public void testDeactivate() { + backend.activate(); + backend.deactivate(); + verify(registrar).unregister(backend); + assertFalse(backend.isActive()); + } + + @Test + public void testDeactivateTwice() { + backend.activate(); + + assertTrue(backend.deactivate()); + assertFalse(backend.isActive()); + assertTrue(backend.deactivate()); + } + + @Test + public void testNewVM() throws MonitorException, URISyntaxException { + int VM_PID = 1; + VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID)); + when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm1); + + backend.vmStatusChanged(Status.VM_STARTED, 1); + + verify(monitoredVm1).addVmListener(isA(VmClassStatVmListener.class)); + } + + @Test + public void testAlreadyRunningVM() throws MonitorException, URISyntaxException { + int VM_PID = 1; + VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID)); + when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm1); + + backend.vmStatusChanged(Status.VM_ACTIVE, 1); + + verify(monitoredVm1).addVmListener(isA(VmClassStatVmListener.class)); + } + + @Test + public void testStatVMGetMonitoredVmFails() throws MonitorException { + MonitorException monitorException = new MonitorException(); + when(host.getMonitoredVm(isA(VmIdentifier.class))).thenThrow(monitorException); + + backend.vmStatusChanged(Status.VM_STARTED, 1); + + assertFalse(backend.getPidToDataMap().containsKey(1)); + } + + @Test + public void testStoppedVM() throws MonitorException, URISyntaxException { + int VM_PID = 1; + VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID)); + when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm1); + + backend.vmStatusChanged(Status.VM_STARTED, 1); + backend.vmStatusChanged(Status.VM_STOPPED, 1); + + verify(monitoredVm1).removeVmListener(isA(VmClassStatVmListener.class)); + } + + @Test + public void testUnknownVMStopped() throws URISyntaxException, MonitorException { + int VM_PID = 1; + VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID)); + when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm1); + + backend.vmStatusChanged(Status.VM_STOPPED, 1); + + verifyNoMoreInteractions(monitoredVm1); + } + + @Test + public void testErrorRemovingVmListener() throws URISyntaxException, MonitorException { + int VM_PID = 1; + VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID)); + when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm1); + MonitorException monitorException = new MonitorException(); + doThrow(monitorException).when(monitoredVm1).removeVmListener(isA(VmClassStatVmListener.class)); + + backend.vmStatusChanged(Status.VM_STARTED, 1); + backend.vmStatusChanged(Status.VM_STOPPED, 1); + + verify(monitoredVm1).detach(); + } + + @Test + public void testOrderValue() { + int orderValue = backend.getOrderValue(); + assertTrue(orderValue > Ordered.ORDER_MEMORY_GROUP); + assertTrue(orderValue < Ordered.ORDER_NETWORK_GROUP); + } } diff -r 8a6697503832 -r 279aeb153688 vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatHostListenerTest.java --- a/vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatHostListenerTest.java Tue Jan 22 16:14:48 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,136 +0,0 @@ -/* - * Copyright 2013 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 - * . - * - * 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.vm.classstat.agent.internal; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.net.URISyntaxException; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import org.junit.Before; -import org.junit.Test; - -import sun.jvmstat.monitor.HostIdentifier; -import sun.jvmstat.monitor.MonitorException; -import sun.jvmstat.monitor.MonitoredHost; -import sun.jvmstat.monitor.MonitoredVm; -import sun.jvmstat.monitor.VmIdentifier; -import sun.jvmstat.monitor.event.VmStatusChangeEvent; - -import com.redhat.thermostat.vm.classstat.common.VmClassStatDAO; - -public class VmClassStatHostListenerTest { - - private VmClassStatHostListener hostListener; - private MonitoredHost host; - private MonitoredVm monitoredVm1; - private MonitoredVm monitoredVm2; - - @Before - public void setup() throws MonitorException, URISyntaxException { - VmClassStatDAO vmGcStatDAO = mock(VmClassStatDAO.class); - hostListener = new VmClassStatHostListener(vmGcStatDAO, true); - - host = mock(MonitoredHost.class); - HostIdentifier hostId = mock(HostIdentifier.class); - monitoredVm1 = mock(MonitoredVm.class); - monitoredVm2 = mock(MonitoredVm.class); - VmIdentifier vmId1 = new VmIdentifier("1"); - VmIdentifier vmId2 = new VmIdentifier("2"); - when(host.getHostIdentifier()).thenReturn(hostId); - when(host.getMonitoredVm(eq(vmId1))).thenReturn(monitoredVm1); - when(host.getMonitoredVm(eq(vmId2))).thenReturn(monitoredVm2); - when(hostId.resolve(eq(vmId1))).thenReturn(vmId1); - when(hostId.resolve(eq(vmId2))).thenReturn(vmId2); - } - - @Test - public void testNewVM() throws InterruptedException, MonitorException { - startVMs(); - - assertTrue(hostListener.getMonitoredVms().containsKey(1)); - assertTrue(hostListener.getMonitoredVms().containsKey(2)); - assertEquals(monitoredVm1, hostListener.getMonitoredVms().get(1)); - assertEquals(monitoredVm2, hostListener.getMonitoredVms().get(2)); - - assertTrue(hostListener.getRegisteredListeners().containsKey(monitoredVm1)); - assertTrue(hostListener.getRegisteredListeners().containsKey(monitoredVm2)); - } - - @Test - public void testStoppedVM() throws InterruptedException, MonitorException { - final Set stopped = new HashSet<>(); - stopped.add(1); - - startVMs(); - - // Trigger a change event - VmStatusChangeEvent event = mock(VmStatusChangeEvent.class); - when(event.getMonitoredHost()).thenReturn(host); - when(event.getStarted()).thenReturn(Collections.emptySet()); - when(event.getTerminated()).thenReturn(stopped); - hostListener.vmStatusChanged(event); - - // Ensure only 1 removed - assertFalse(hostListener.getMonitoredVms().containsKey(1)); - assertTrue(hostListener.getMonitoredVms().containsKey(2)); - assertEquals(monitoredVm2, hostListener.getMonitoredVms().get(2)); - - assertFalse(hostListener.getRegisteredListeners().containsKey(monitoredVm1)); - assertTrue(hostListener.getRegisteredListeners().containsKey(monitoredVm2)); - } - - private void startVMs() throws InterruptedException, MonitorException { - final Set started = new HashSet<>(); - started.add(1); - started.add(2); - - // Trigger a change event - VmStatusChangeEvent event = mock(VmStatusChangeEvent.class); - when(event.getMonitoredHost()).thenReturn(host); - when(event.getStarted()).thenReturn(started); - when(event.getTerminated()).thenReturn(Collections.emptySet()); - hostListener.vmStatusChanged(event); - } -} diff -r 8a6697503832 -r 279aeb153688 vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatVmListenerTest.java --- a/vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatVmListenerTest.java Tue Jan 22 16:14:48 2013 +0100 +++ b/vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatVmListenerTest.java Tue Jan 22 16:15:32 2013 +0100 @@ -37,15 +37,20 @@ package com.redhat.thermostat.vm.classstat.agent.internal; import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import sun.jvmstat.monitor.Monitor; +import sun.jvmstat.monitor.MonitorException; import sun.jvmstat.monitor.MonitoredVm; +import sun.jvmstat.monitor.event.MonitorStatusChangeEvent; import sun.jvmstat.monitor.event.VmEvent; import com.redhat.thermostat.storage.model.VmClassStat; @@ -57,12 +62,35 @@ private static final Integer VM_ID = 123; private static final Long LOADED_CLASSES = 1234L; + private VmClassStatDAO dao; + private VmClassStatVmListener listener; + + @Before + public void setUp() { + dao = mock(VmClassStatDAO.class); + listener = new VmClassStatVmListener(dao, VM_ID); + } + + @Test + public void testDisconnected() { + VmEvent vmEvent = mock(VmEvent.class); + + listener.disconnected(vmEvent); + + verifyNoMoreInteractions(vmEvent, dao); + } + + @Test + public void testMonitorStatusChanged() { + MonitorStatusChangeEvent statusChangeEvent = mock(MonitorStatusChangeEvent.class); + + listener.monitorStatusChanged(statusChangeEvent); + + verifyNoMoreInteractions(statusChangeEvent, dao); + } + @Test public void testMonitorUpdatedClassStat() throws Exception { - - VmClassStatDAO dao = mock(VmClassStatDAO.class); - - VmClassStatVmListener l = new VmClassStatVmListener(dao, VM_ID); VmEvent vmEvent = mock(VmEvent.class); MonitoredVm monitoredVm = mock(MonitoredVm.class); Monitor m = mock(Monitor.class); @@ -70,7 +98,7 @@ when(monitoredVm.findByName("java.cls.loadedClasses")).thenReturn(m); when(vmEvent.getMonitoredVm()).thenReturn(monitoredVm); - l.monitorsUpdated(vmEvent); + listener.monitorsUpdated(vmEvent); ArgumentCaptor arg = ArgumentCaptor.forClass(VmClassStat.class); verify(dao).putVmClassStat(arg.capture()); @@ -81,10 +109,6 @@ @Test public void testMonitorUpdatedClassStatTwice() throws Exception { - - VmClassStatDAO dao = mock(VmClassStatDAO.class); - - VmClassStatVmListener l = new VmClassStatVmListener(dao, VM_ID); VmEvent vmEvent = mock(VmEvent.class); MonitoredVm monitoredVm = mock(MonitoredVm.class); Monitor m = mock(Monitor.class); @@ -92,10 +116,24 @@ when(monitoredVm.findByName("java.cls.loadedClasses")).thenReturn(m); when(vmEvent.getMonitoredVm()).thenReturn(monitoredVm); - l.monitorsUpdated(vmEvent); - l.monitorsUpdated(vmEvent); + listener.monitorsUpdated(vmEvent); + listener.monitorsUpdated(vmEvent); // This checks a bug where the Category threw an IllegalStateException because the DAO // created a new one on each call, thus violating the unique guarantee of Category. } + + @Test + public void testMonitorUpdateFails() throws MonitorException { + VmEvent vmEvent = mock(VmEvent.class); + MonitoredVm monitoredVm = mock(MonitoredVm.class); + MonitorException monitorException = new MonitorException(); + + when(monitoredVm.findByName(anyString())).thenThrow(monitorException); + when(vmEvent.getMonitoredVm()).thenReturn(monitoredVm); + + listener.monitorsUpdated(vmEvent); + + verifyNoMoreInteractions(dao); + } } diff -r 8a6697503832 -r 279aeb153688 vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/Activator.java --- a/vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/Activator.java Tue Jan 22 16:14:48 2013 +0100 +++ b/vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/Activator.java Tue Jan 22 16:15:32 2013 +0100 @@ -44,6 +44,7 @@ import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; +import com.redhat.thermostat.agent.VmStatusListenerRegistrar; import com.redhat.thermostat.backend.Backend; import com.redhat.thermostat.backend.BackendService; import com.redhat.thermostat.common.MultipleServiceTracker; @@ -60,6 +61,8 @@ @Override public void start(final BundleContext context) throws Exception { + final VmStatusListenerRegistrar registrar = new VmStatusListenerRegistrar(context); + executor = Executors.newSingleThreadScheduledExecutor(); Class[] deps = new Class[] { @@ -72,7 +75,7 @@ public void dependenciesAvailable(Map services) { VmCpuStatDAO vmCpuStatDao = (VmCpuStatDAO) services.get(VmCpuStatDAO.class.getName()); Version version = new Version(context.getBundle()); - backend = new VmCpuBackend(executor, vmCpuStatDao, version); + backend = new VmCpuBackend(executor, vmCpuStatDao, version, registrar); reg = context.registerService(Backend.class.getName(), backend, null); } diff -r 8a6697503832 -r 279aeb153688 vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackend.java --- a/vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackend.java Tue Jan 22 16:14:48 2013 +0100 +++ b/vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackend.java Tue Jan 22 16:15:32 2013 +0100 @@ -38,16 +38,15 @@ import java.io.BufferedReader; import java.io.IOException; -import java.net.URISyntaxException; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; -import sun.jvmstat.monitor.HostIdentifier; -import sun.jvmstat.monitor.MonitorException; -import sun.jvmstat.monitor.MonitoredHost; - +import com.redhat.thermostat.agent.VmStatusListener; +import com.redhat.thermostat.agent.VmStatusListenerRegistrar; import com.redhat.thermostat.backend.Backend; import com.redhat.thermostat.backend.BackendID; import com.redhat.thermostat.backend.BackendsProperties; @@ -60,7 +59,7 @@ import com.redhat.thermostat.utils.SysConf; import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO; -public class VmCpuBackend extends Backend { +public class VmCpuBackend extends Backend implements VmStatusListener { private static final Logger LOGGER = LoggingUtils.getLogger(VmCpuBackend.class); static final long PROC_CHECK_INTERVAL = 1000; // TODO make this configurable. @@ -68,15 +67,17 @@ private VmCpuStatBuilder vmCpuStatBuilder; private VmCpuStatDAO vmCpuStats; private ScheduledExecutorService executor; - private HostIdentifier hostId; - private MonitoredHost host; - private VmCpuHostListener hostListener; + private VmStatusListenerRegistrar registrar; private boolean started; - public VmCpuBackend(ScheduledExecutorService executor, VmCpuStatDAO vmCpuStatDao, Version version) { + private final List pidsToMonitor = new CopyOnWriteArrayList<>(); + + public VmCpuBackend(ScheduledExecutorService executor, VmCpuStatDAO vmCpuStatDao, Version version, + VmStatusListenerRegistrar registrar) { super(new BackendID("VM CPU Backend", VmCpuBackend.class.getName())); this.executor = executor; this.vmCpuStats = vmCpuStatDao; + this.registrar = registrar; setConfigurationValue(BackendsProperties.VENDOR.name(), "Red Hat, Inc."); setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers CPU statistics about a JVM"); @@ -88,25 +89,17 @@ ProcessStatusInfoBuilder builder = new ProcessStatusInfoBuilder(new ProcDataSource()); int numCpus = getCpuCount(source); vmCpuStatBuilder = new VmCpuStatBuilder(clock, numCpus, ticksPerSecond, builder); - - try { - hostId = new HostIdentifier((String) null); - host = MonitoredHost.getMonitoredHost(hostId); - hostListener = new VmCpuHostListener(vmCpuStatBuilder); - } catch (MonitorException me) { - LOGGER.log(Level.WARNING, "Problems with connecting jvmstat to local machine", me); - } catch (URISyntaxException use) { - LOGGER.log(Level.WARNING, "Failed to create host identifier", use); - } } @Override public boolean activate() { - if (!started && host != null) { + if (!started) { + registrar.register(this); + executor.scheduleAtFixedRate(new Runnable() { @Override public void run() { - for (Integer pid : hostListener.getPidsToMonitor()) { + for (Integer pid : pidsToMonitor) { if (vmCpuStatBuilder.knowsAbout(pid)) { VmCpuStat dataBuilt = vmCpuStatBuilder.build(pid); if (dataBuilt != null) { @@ -119,28 +112,18 @@ } }, 0, PROC_CHECK_INTERVAL, TimeUnit.MILLISECONDS); - try { - host.addHostListener(hostListener); - started = true; - } catch (MonitorException me) { - LOGGER.log(Level.WARNING, "Failed to add host listener", me); - } - + started = true; } return started; } @Override public boolean deactivate() { - if (started && host != null) { + if (started) { executor.shutdown(); + registrar.unregister(this); - try { - host.removeHostListener(hostListener); - started = false; - } catch (MonitorException me) { - LOGGER.log(Level.INFO, "Failed to remove host listener"); - } + started = false; } return !started; } @@ -183,10 +166,23 @@ } /* - * For testing purposes only. + * Methods implementing VmStatusListener */ - void setHost(MonitoredHost host) { - this.host = host; + @Override + public void vmStatusChanged(Status newStatus, int pid) { + switch (newStatus) { + case VM_STARTED: + /* fall-through */ + case VM_ACTIVE: + pidsToMonitor.add(pid); + break; + case VM_STOPPED: + // the cast is important because it changes the call from remove(index) to remove(Object) + pidsToMonitor.remove((Integer) pid); + vmCpuStatBuilder.forgetAbout(pid); + break; + } + } /* @@ -195,12 +191,5 @@ void setVmCpuStatBuilder(VmCpuStatBuilder vmCpuStatBuilder) { this.vmCpuStatBuilder = vmCpuStatBuilder; } - - /* - * For testing purposes only. - */ - void setHostListener(VmCpuHostListener hostListener) { - this.hostListener = hostListener; - } - + } diff -r 8a6697503832 -r 279aeb153688 vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuHostListener.java --- a/vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuHostListener.java Tue Jan 22 16:14:48 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +0,0 @@ -/* - * Copyright 2013 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 - * . - * - * 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.vm.cpu.agent.internal; - -import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.logging.Logger; - -import sun.jvmstat.monitor.event.HostEvent; -import sun.jvmstat.monitor.event.HostListener; -import sun.jvmstat.monitor.event.VmStatusChangeEvent; - -import com.redhat.thermostat.common.utils.LoggingUtils; - -public class VmCpuHostListener implements HostListener { - - private static final Logger LOGGER = LoggingUtils.getLogger(VmCpuHostListener.class); - - private final Set pidsToMonitor = new CopyOnWriteArraySet(); - private VmCpuStatBuilder vmCpuStatBuilder; - - public VmCpuHostListener(VmCpuStatBuilder builder) { - this.vmCpuStatBuilder = builder; - } - - @Override - public void vmStatusChanged(VmStatusChangeEvent event) { - for (Object newVm : event.getStarted()) { - Integer vmId = (Integer) newVm; - LOGGER.fine("New vm: " + vmId); - pidsToMonitor.add(vmId); - } - - for (Object stoppedVm : event.getTerminated()) { - Integer vmId = (Integer) stoppedVm; - LOGGER.fine("stopped vm: " + vmId); - pidsToMonitor.remove(vmId); - vmCpuStatBuilder.forgetAbout(vmId); - } - } - - @Override - public void disconnected(HostEvent event) { - LOGGER.warning("Disconnected from host"); - } - - public Set getPidsToMonitor() { - return pidsToMonitor; - } - -} diff -r 8a6697503832 -r 279aeb153688 vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/ActivatorTest.java --- a/vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/ActivatorTest.java Tue Jan 22 16:14:48 2013 +0100 +++ b/vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/ActivatorTest.java Tue Jan 22 16:15:32 2013 +0100 @@ -93,6 +93,9 @@ VmCpuBackend backend = activator.getBackend(); assertNotNull(backend); + // core thermostat will activate the backend once it's registered + backend.activate(); + activator.stop(context); assertFalse(backend.isActive()); diff -r 8a6697503832 -r 279aeb153688 vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackendTest.java --- a/vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackendTest.java Tue Jan 22 16:14:48 2013 +0100 +++ b/vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackendTest.java Tue Jan 22 16:15:32 2013 +0100 @@ -40,8 +40,11 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import java.util.HashSet; @@ -53,10 +56,9 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; -import sun.jvmstat.monitor.MonitorException; -import sun.jvmstat.monitor.MonitoredHost; -import sun.jvmstat.monitor.event.HostListener; - +import com.redhat.thermostat.agent.VmStatusListener.Status; +import com.redhat.thermostat.agent.VmStatusListenerRegistrar; +import com.redhat.thermostat.common.Ordered; import com.redhat.thermostat.common.Version; import com.redhat.thermostat.storage.model.VmCpuStat; import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO; @@ -65,8 +67,8 @@ private VmCpuBackend backend; private ScheduledExecutorService executor; - private MonitoredHost host; private VmCpuStatDAO vmCpuStatDao; + private VmStatusListenerRegistrar registrar; @Before public void setup() { @@ -76,21 +78,58 @@ Version version = mock(Version.class); when(version.getVersionNumber()).thenReturn("0.0.0"); - backend = new VmCpuBackend(executor, vmCpuStatDao, version); + registrar = mock(VmStatusListenerRegistrar.class); - host = mock(MonitoredHost.class); - backend.setHost(host); + backend = new VmCpuBackend(executor, vmCpuStatDao, version, registrar); + } + + @Test + public void testActivate() { + backend.activate(); + + verify(executor).scheduleAtFixedRate(isA(Runnable.class), eq(0l), eq(1000l), eq(TimeUnit.MILLISECONDS)); + verify(registrar).register(backend); + assertTrue(backend.isActive()); } @Test - public void testStart() throws MonitorException { + public void testActivateTwice() { + assertTrue(backend.activate()); + assertTrue(backend.isActive()); + assertTrue(backend.activate()); + assertTrue(backend.isActive()); + + assertTrue(backend.deactivate()); + } + + @Test + public void testDeactivate() { + backend.activate(); + backend.deactivate(); + + verify(executor).shutdown(); + verify(registrar).unregister(backend); + assertFalse(backend.isActive()); + } + + + @Test + public void testDeactivateTwice() { + assertTrue(backend.activate()); + assertTrue(backend.isActive()); + + assertTrue(backend.deactivate()); + assertFalse(backend.isActive()); + assertTrue(backend.deactivate()); + assertFalse(backend.isActive()); + } + + @Test + public void testStart() { // Setup Runnable mocks final Set pids = new HashSet<>(); pids.add(0); pids.add(1); - VmCpuHostListener listener = mock(VmCpuHostListener.class); - when(listener.getPidsToMonitor()).thenReturn(pids); - backend.setHostListener(listener); VmCpuStatBuilder builder = mock(VmCpuStatBuilder.class); VmCpuStat stat0 = mock(VmCpuStat.class); @@ -100,11 +139,15 @@ backend.setVmCpuStatBuilder(builder); backend.activate(); + + verify(registrar).register(backend); ArgumentCaptor captor = ArgumentCaptor.forClass(Runnable.class); verify(executor).scheduleAtFixedRate(captor.capture(), any(Long.class), any(Long.class), any(TimeUnit.class)); assertTrue(backend.isActive()); - verify(host).addHostListener(any(HostListener.class)); + backend.vmStatusChanged(Status.VM_ACTIVE, 0); + backend.vmStatusChanged(Status.VM_STARTED, 1); + Runnable runnable = captor.getValue(); runnable.run(); verify(builder).learnAbout(0); @@ -114,15 +157,24 @@ runnable.run(); verify(vmCpuStatDao).putVmCpuStat(stat0); verify(vmCpuStatDao).putVmCpuStat(stat1); + + backend.vmStatusChanged(Status.VM_STOPPED, 0); + backend.vmStatusChanged(Status.VM_STOPPED, 1); + + verify(builder).forgetAbout(0); + verify(builder).forgetAbout(1); + + when(builder.knowsAbout(anyInt())).thenReturn(false); + runnable.run(); + + verifyNoMoreInteractions(vmCpuStatDao); } - + @Test - public void testStop() throws MonitorException { - backend.activate(); - backend.deactivate(); - verify(executor).shutdown(); - assertFalse(backend.isActive()); - verify(host).removeHostListener(any(HostListener.class)); + public void testOrderValue() { + int orderValue = backend.getOrderValue(); + + assertTrue(orderValue > Ordered.ORDER_CPU_GROUP); + assertTrue(orderValue < Ordered.ORDER_MEMORY_GROUP); } - } diff -r 8a6697503832 -r 279aeb153688 vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuHostListenerTest.java --- a/vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuHostListenerTest.java Tue Jan 22 16:14:48 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,106 +0,0 @@ -/* - * Copyright 2013 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 - * . - * - * 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.vm.cpu.agent.internal; - -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import org.junit.Before; -import org.junit.Test; - -import sun.jvmstat.monitor.event.VmStatusChangeEvent; - -public class VmCpuHostListenerTest { - - private VmCpuHostListener hostListener; - private VmCpuStatBuilder builder; - - @Before - public void setup() { - builder = mock(VmCpuStatBuilder.class); - - hostListener = new VmCpuHostListener(builder); - } - - @Test - public void testNewVM() throws InterruptedException { - startVMs(); - - // Check that pids are added to set - Set pids = hostListener.getPidsToMonitor(); - assertTrue(pids.contains(1)); - assertTrue(pids.contains(2)); - } - - @Test - public void testStoppedVM() throws InterruptedException { - final Set stopped = new HashSet<>(); - stopped.add(1); - - startVMs(); - - // Trigger a change event - VmStatusChangeEvent event = mock(VmStatusChangeEvent.class); - when(event.getStarted()).thenReturn(Collections.emptySet()); - when(event.getTerminated()).thenReturn(stopped); - hostListener.vmStatusChanged(event); - - // Ensure only 1 removed - verify(builder).forgetAbout(1); - verify(builder, never()).forgetAbout(2); - } - - private void startVMs() throws InterruptedException { - final Set started = new HashSet<>(); - started.add(1); - started.add(2); - - // Trigger a change event - VmStatusChangeEvent event = mock(VmStatusChangeEvent.class); - when(event.getStarted()).thenReturn(started); - when(event.getTerminated()).thenReturn(Collections.emptySet()); - hostListener.vmStatusChanged(event); - } - -} diff -r 8a6697503832 -r 279aeb153688 vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackend.java --- a/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackend.java Tue Jan 22 16:14:48 2013 +0100 +++ b/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackend.java Tue Jan 22 16:15:32 2013 +0100 @@ -54,6 +54,7 @@ import com.redhat.thermostat.backend.Backend; import com.redhat.thermostat.backend.BackendID; import com.redhat.thermostat.backend.BackendsProperties; +import com.redhat.thermostat.common.Pair; import com.redhat.thermostat.common.Version; import com.redhat.thermostat.common.utils.LoggingUtils; import com.redhat.thermostat.vm.gc.common.VmGcStatDAO; @@ -65,7 +66,7 @@ private final VmGcStatDAO vmGcStats; private final VmStatusListenerRegistrar registerer; - private final Map registeredListeners = new HashMap<>(); + private final Map> pidToData = new HashMap<>(); private MonitoredHost host; private boolean started; @@ -92,7 +93,7 @@ @Override public boolean activate() { - if (!started) { + if (!started && host != null) { registerer.register(this); started = true; } @@ -151,7 +152,7 @@ if (vm != null) { VmGcVmListener listener = new VmGcVmListener(vmGcStats, pid); vm.addVmListener(listener); - registeredListeners.put(pid, new VmAndListener(vm, listener)); + pidToData.put(pid, new Pair<>(vm, listener)); LOGGER.finer("Attached VmListener for VM: " + pid); } else { LOGGER.warning("could not connect to vm " + pid); @@ -165,14 +166,14 @@ } private void vmStopped(int pid) { - VmAndListener tuple = registeredListeners.remove(pid); - if (tuple == null) { - LOGGER.warning("received vm stopped for an unknown VM"); + Pair data = pidToData.remove(pid); + // if there is no data, we must never have attached to it. Nothing to do. + if (data == null) { return; } - MonitoredVm vm = tuple.vm; - VmListener listener = tuple.listener; + MonitoredVm vm = data.getFirst(); + VmListener listener = data.getSecond(); try { if (listener != null) { vm.removeVmListener(listener); @@ -190,14 +191,4 @@ this.host = host; } - private static class VmAndListener { - private MonitoredVm vm; - private VmListener listener; - - public VmAndListener(MonitoredVm vm, VmListener listener) { - this.vm = vm; - this.listener = listener; - } - - } } diff -r 8a6697503832 -r 279aeb153688 vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/Activator.java --- a/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/Activator.java Tue Jan 22 16:14:48 2013 +0100 +++ b/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/Activator.java Tue Jan 22 16:15:32 2013 +0100 @@ -42,6 +42,7 @@ import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; +import com.redhat.thermostat.agent.VmStatusListenerRegistrar; import com.redhat.thermostat.backend.Backend; import com.redhat.thermostat.backend.BackendService; import com.redhat.thermostat.common.MultipleServiceTracker; @@ -57,6 +58,9 @@ @Override public void start(final BundleContext context) throws Exception { + + final VmStatusListenerRegistrar registrar = new VmStatusListenerRegistrar(context); + Class[] deps = new Class[] { BackendService.class, VmMemoryStatDAO.class @@ -67,7 +71,7 @@ public void dependenciesAvailable(Map services) { VmMemoryStatDAO vmMemoryStatDao = (VmMemoryStatDAO) services.get(VmMemoryStatDAO.class.getName()); Version version = new Version(context.getBundle()); - backend = new VmMemoryBackend(vmMemoryStatDao, version); + backend = new VmMemoryBackend(vmMemoryStatDao, version, registrar); reg = context.registerService(Backend.class.getName(), backend, null); } diff -r 8a6697503832 -r 279aeb153688 vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackend.java --- a/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackend.java Tue Jan 22 16:14:48 2013 +0100 +++ b/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackend.java Tue Jan 22 16:15:32 2013 +0100 @@ -37,42 +37,51 @@ package com.redhat.thermostat.vm.memory.agent.internal; import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import sun.jvmstat.monitor.HostIdentifier; import sun.jvmstat.monitor.MonitorException; import sun.jvmstat.monitor.MonitoredHost; +import sun.jvmstat.monitor.MonitoredVm; +import sun.jvmstat.monitor.VmIdentifier; +import sun.jvmstat.monitor.event.VmListener; +import com.redhat.thermostat.agent.VmStatusListener; +import com.redhat.thermostat.agent.VmStatusListenerRegistrar; import com.redhat.thermostat.backend.Backend; import com.redhat.thermostat.backend.BackendID; import com.redhat.thermostat.backend.BackendsProperties; +import com.redhat.thermostat.common.Pair; import com.redhat.thermostat.common.Version; import com.redhat.thermostat.common.utils.LoggingUtils; import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO; -public class VmMemoryBackend extends Backend { +public class VmMemoryBackend extends Backend implements VmStatusListener { private static final Logger LOGGER = LoggingUtils.getLogger(VmMemoryBackend.class); private VmMemoryStatDAO vmMemoryStats; - private HostIdentifier hostId; private MonitoredHost host; - private VmMemoryHostListener hostListener; + private final VmStatusListenerRegistrar registrar; + private boolean started; + private Map> pidToData = new HashMap<>(); - public VmMemoryBackend(VmMemoryStatDAO vmMemoryStatDAO, Version version) { + public VmMemoryBackend(VmMemoryStatDAO vmMemoryStatDAO, Version version, VmStatusListenerRegistrar registrar) { super(new BackendID("VM Memory Backend", VmMemoryBackend.class.getName())); this.vmMemoryStats = vmMemoryStatDAO; + this.registrar = registrar; setConfigurationValue(BackendsProperties.VENDOR.name(), "Red Hat, Inc."); setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers memory statistics about a JVM"); setConfigurationValue(BackendsProperties.VERSION.name(), version.getVersionNumber()); try { - hostId = new HostIdentifier((String) null); + HostIdentifier hostId = new HostIdentifier((String) null); host = MonitoredHost.getMonitoredHost(hostId); - hostListener = new VmMemoryHostListener(vmMemoryStats, attachToNewProcessByDefault()); } catch (MonitorException me) { LOGGER.log(Level.WARNING, "Problems with connecting jvmstat to local machine", me); } catch (URISyntaxException use) { @@ -83,25 +92,17 @@ @Override public boolean activate() { if (!started && host != null) { - try { - host.addHostListener(hostListener); - started = true; - } catch (MonitorException me) { - LOGGER.log(Level.WARNING, "Failed to add host listener", me); - } + registrar.register(this); + started = true; } return started; } @Override public boolean deactivate() { - if (started && host != null) { - try { - host.removeHostListener(hostListener); - started = false; - } catch (MonitorException me) { - LOGGER.log(Level.INFO, "Failed to remove host listener"); - } + if (started) { + registrar.unregister(this); + started = false; } return !started; } @@ -127,6 +128,58 @@ } /* + * Methods for VmStatusListener + */ + public void vmStatusChanged(Status newStatus, int pid) { + switch (newStatus) { + case VM_STARTED: + /* fall-through */ + case VM_ACTIVE: + handleNewVm(pid); + break; + case VM_STOPPED: + handleStoppedVm(pid); + break; + default: + break; + } + }; + + private void handleNewVm(int pid) { + if (attachToNewProcessByDefault()) { + try { + MonitoredVm vm = host.getMonitoredVm(host.getHostIdentifier().resolve(new VmIdentifier(String.valueOf(pid)))); + VmMemoryVmListener listener = new VmMemoryVmListener(vmMemoryStats, pid); + vm.addVmListener(listener); + + pidToData.put(pid, new Pair<>(vm, listener)); + LOGGER.finer("Attached VmListener for VM: " + pid); + } catch (MonitorException | URISyntaxException e) { + LOGGER.log(Level.WARNING, "unable to attach to vm " + pid, e); + } + } else { + LOGGER.log(Level.FINE, "skipping new vm " + pid); + } + } + + private void handleStoppedVm(int pid) { + Pair data = pidToData.remove(pid); + // we were not monitoring pid at all, so nothing to do + if (data == null) { + return; + } + + MonitoredVm vm = data.getFirst(); + VmListener listener = data.getSecond(); + try { + vm.removeVmListener(listener); + } catch (MonitorException e) { + LOGGER.log(Level.WARNING, "can't remove vm listener", e); + } + vm.detach(); + } + + /* * For testing purposes only. */ void setHost(MonitoredHost host) { diff -r 8a6697503832 -r 279aeb153688 vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryHostListener.java --- a/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryHostListener.java Tue Jan 22 16:14:48 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,172 +0,0 @@ -/* - * Copyright 2013 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 - * . - * - * 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.vm.memory.agent.internal; - -import java.net.URISyntaxException; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; - -import sun.jvmstat.monitor.MonitorException; -import sun.jvmstat.monitor.MonitoredHost; -import sun.jvmstat.monitor.MonitoredVm; -import sun.jvmstat.monitor.VmIdentifier; -import sun.jvmstat.monitor.event.HostEvent; -import sun.jvmstat.monitor.event.HostListener; -import sun.jvmstat.monitor.event.VmListener; -import sun.jvmstat.monitor.event.VmStatusChangeEvent; - -import com.redhat.thermostat.common.utils.LoggingUtils; -import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO; - -public class VmMemoryHostListener implements HostListener { - - private static final Logger logger = LoggingUtils.getLogger(VmMemoryHostListener.class); - - private boolean attachNew; - - private final VmMemoryStatDAO vmMemoryStatDAO; - - private Map monitoredVms = new HashMap<>(); - private Map registeredListeners = new ConcurrentHashMap<>(); - - VmMemoryHostListener(VmMemoryStatDAO vmMemoryStatDAO, boolean attachNew) { - this.vmMemoryStatDAO = vmMemoryStatDAO; - this.attachNew = attachNew; - } - - void removeAllListeners() { - for (MonitoredVm vm : monitoredVms.values()) { - VmListener listener = registeredListeners.get(vm); - try { - if (listener != null) { - vm.removeVmListener(listener); - } - } catch (MonitorException e) { - logger.log(Level.WARNING, "can't remove vm listener", e); - } - } - } - - @Override - public void disconnected(HostEvent event) { - logger.warning("Disconnected from host"); - } - - @SuppressWarnings("unchecked") // Unchecked casts to (Set). - @Override - public void vmStatusChanged(VmStatusChangeEvent event) { - MonitoredHost host = event.getMonitoredHost(); - - for (Integer newVm : (Set) event.getStarted()) { - try { - logger.fine("New vm: " + newVm); - sendNewVM(newVm, host); - } catch (MonitorException e) { - logger.log(Level.WARNING, "error getting info for new vm" + newVm, e); - } catch (URISyntaxException e) { - logger.log(Level.WARNING, "error getting info for new vm" + newVm, e); - } - } - - for (Integer stoppedVm : (Set) event.getTerminated()) { - try { - logger.fine("stopped vm: " + stoppedVm); - sendStoppedVM(stoppedVm, host); - } catch (URISyntaxException e) { - logger.log(Level.WARNING, "error getting info for stopped vm" + stoppedVm, e); - } catch (MonitorException e) { - logger.log(Level.WARNING, "error getting info for stopped vm" + stoppedVm, e); - } - } - } - - private void sendNewVM(Integer vmId, MonitoredHost host) - throws MonitorException, URISyntaxException { - MonitoredVm vm = host.getMonitoredVm(host.getHostIdentifier().resolve( - new VmIdentifier(vmId.toString()))); - if (vm != null) { - - if (attachNew) { - VmMemoryVmListener listener = new VmMemoryVmListener(vmMemoryStatDAO, vmId); - vm.addVmListener(listener); - registeredListeners.put(vm, listener); - logger.finer("Attached VmListener for VM: " + vmId); - - } else { - logger.log(Level.FINE, "skipping new vm " + vmId); - } - - monitoredVms.put(vmId, vm); - } - } - - private void sendStoppedVM(Integer vmId, MonitoredHost host) throws URISyntaxException, MonitorException { - - VmIdentifier resolvedVmID = host.getHostIdentifier().resolve(new VmIdentifier(vmId.toString())); - if (resolvedVmID != null) { - MonitoredVm vm = monitoredVms.remove(vmId); - VmMemoryVmListener listener = registeredListeners.remove(vm); - try { - if (listener != null) { - vm.removeVmListener(listener); - } - } catch (MonitorException e) { - logger.log(Level.WARNING, "can't remove vm listener", e); - } - vm.detach(); - } - } - - /* - * For testing purposes only. - */ - Map getMonitoredVms() { - return monitoredVms; - } - - /* - * For testing purposes only. - */ - Map getRegisteredListeners() { - return registeredListeners; - } - -} diff -r 8a6697503832 -r 279aeb153688 vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryVmListener.java --- a/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryVmListener.java Tue Jan 22 16:14:48 2013 +0100 +++ b/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryVmListener.java Tue Jan 22 16:15:32 2013 +0100 @@ -76,15 +76,12 @@ @Override public void monitorsUpdated(VmEvent event) { MonitoredVm vm = event.getMonitoredVm(); - if (vm == null) { - throw new NullPointerException(); - } - + VmMemoryDataExtractor extractor = new VmMemoryDataExtractor(vm); - recordMemoryStat(vm, extractor); + recordMemoryStat(extractor); } - void recordMemoryStat(MonitoredVm vm, VmMemoryDataExtractor extractor) { + void recordMemoryStat(VmMemoryDataExtractor extractor) { try { long timestamp = System.currentTimeMillis(); int maxGenerations = (int) extractor.getTotalGcGenerations(); diff -r 8a6697503832 -r 279aeb153688 vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/ActivatorTest.java --- a/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/ActivatorTest.java Tue Jan 22 16:14:48 2013 +0100 +++ b/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/ActivatorTest.java Tue Jan 22 16:15:32 2013 +0100 @@ -93,6 +93,10 @@ VmMemoryBackend backend = activator.getBackend(); assertNotNull(backend); + // core thermostat activates the backend when the backend is detected + // do it manually for the test + backend.activate(); + activator.stop(context); assertFalse(backend.isActive()); diff -r 8a6697503832 -r 279aeb153688 vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackendTest.java --- a/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackendTest.java Tue Jan 22 16:14:48 2013 +0100 +++ b/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackendTest.java Tue Jan 22 16:15:32 2013 +0100 @@ -38,20 +38,31 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import java.net.URISyntaxException; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import sun.jvmstat.monitor.HostIdentifier; import sun.jvmstat.monitor.MonitorException; import sun.jvmstat.monitor.MonitoredHost; -import sun.jvmstat.monitor.event.HostListener; +import sun.jvmstat.monitor.MonitoredVm; +import sun.jvmstat.monitor.VmIdentifier; +import sun.jvmstat.monitor.event.VmListener; +import com.redhat.thermostat.agent.VmStatusListener.Status; +import com.redhat.thermostat.agent.VmStatusListenerRegistrar; +import com.redhat.thermostat.common.Ordered; import com.redhat.thermostat.common.Version; import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO; @@ -59,32 +70,158 @@ private VmMemoryBackend backend; private MonitoredHost host; + private VmStatusListenerRegistrar registrar; + private VmMemoryStatDAO vmMemoryStatDao; @Before public void setup() throws MonitorException, URISyntaxException { - VmMemoryStatDAO vmMemoryStatDao = mock(VmMemoryStatDAO.class); + vmMemoryStatDao = mock(VmMemoryStatDAO.class); Version version = mock(Version.class); when(version.getVersionNumber()).thenReturn("0.0.0"); - backend = new VmMemoryBackend(vmMemoryStatDao, version); + + registrar = mock(VmStatusListenerRegistrar.class); + + backend = new VmMemoryBackend(vmMemoryStatDao, version, registrar); + HostIdentifier hostIdentifier = mock(HostIdentifier.class); + when(hostIdentifier.resolve(isA(VmIdentifier.class))).then(new Answer() { + @Override + public VmIdentifier answer(InvocationOnMock invocation) throws Throwable { + return (VmIdentifier) invocation.getArguments()[0]; + } + }); host = mock(MonitoredHost.class); + when(host.getHostIdentifier()).thenReturn(hostIdentifier); + backend.setHost(host); } @Test - public void testStart() throws MonitorException { - backend.activate(); - verify(host).addHostListener(any(HostListener.class)); + public void testActivate() { + assertTrue(backend.activate()); + + verify(registrar).register(backend); assertTrue(backend.isActive()); } @Test - public void testStop() throws MonitorException { + public void testActivateTwice() { + assertTrue(backend.activate()); + assertTrue(backend.isActive()); + + assertTrue(backend.activate()); + assertTrue(backend.isActive()); + + verify(registrar).register(backend); + } + + @Test + public void testActivateFailsIfHostIsNull() { + backend.setHost(null); + + assertFalse(backend.activate()); + } + + @Test + public void testDeactivate() { backend.activate(); backend.deactivate(); - verify(host).removeHostListener(any(HostListener.class)); + + verify(registrar).unregister(backend); assertFalse(backend.isActive()); } - + + @Test + public void testDeactiveTwice() { + assertTrue(backend.activate()); + + assertTrue(backend.deactivate()); + assertFalse(backend.isActive()); + + assertTrue(backend.deactivate()); + assertFalse(backend.isActive()); + + verify(registrar).unregister(backend); + } + + @Test + public void testNewVmStarted() throws URISyntaxException, MonitorException { + int VM_PID = 10; + VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID)); + MonitoredVm monitoredVm = mock(MonitoredVm.class); + + when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm); + + backend.vmStatusChanged(Status.VM_STARTED, VM_PID); + + verify(monitoredVm).addVmListener(isA(VmMemoryVmListener.class)); + } + + @Test + public void testErrorInAttachingToNewVm() throws MonitorException, URISyntaxException { + int VM_PID = 10; + VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID)); + + when(host.getMonitoredVm(VM_ID)).thenThrow(new MonitorException()); + + backend.vmStatusChanged(Status.VM_STARTED, VM_PID); + } + + @Test + public void testVmStopped() throws URISyntaxException, MonitorException { + int VM_PID = 10; + VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID)); + MonitoredVm monitoredVm = mock(MonitoredVm.class); + + when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm); + + backend.vmStatusChanged(Status.VM_STARTED, VM_PID); + + ArgumentCaptor listenerCaptor = ArgumentCaptor.forClass(VmListener.class); + verify(monitoredVm).addVmListener(listenerCaptor.capture()); + + backend.vmStatusChanged(Status.VM_STOPPED, VM_PID); + + verify(monitoredVm).removeVmListener(listenerCaptor.getValue()); + verify(monitoredVm).detach(); + } + + @Test + public void testUnknownVmStoppedIsIgnored() { + int VM_PID = 10; + + backend.vmStatusChanged(Status.VM_STOPPED, VM_PID); + + verifyNoMoreInteractions(host, vmMemoryStatDao); + } + + @Test + public void testStoppedVmIsDetachedEvenInPresenceOfErrors() throws URISyntaxException, MonitorException { + int VM_PID = 10; + VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID)); + MonitoredVm monitoredVm = mock(MonitoredVm.class); + + when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm); + + backend.vmStatusChanged(Status.VM_STARTED, VM_PID); + + ArgumentCaptor listenerCaptor = ArgumentCaptor.forClass(VmListener.class); + verify(monitoredVm).addVmListener(listenerCaptor.capture()); + + VmListener vmListener = listenerCaptor.getValue(); + doThrow(new MonitorException("test")).when(monitoredVm).removeVmListener(vmListener); + + backend.vmStatusChanged(Status.VM_STOPPED, VM_PID); + + verify(monitoredVm).detach(); + } + + @Test + public void testOrderValue() { + int orderValue = backend.getOrderValue(); + + assertTrue(orderValue > Ordered.ORDER_MEMORY_GROUP); + assertTrue(orderValue < Ordered.ORDER_NETWORK_GROUP); + } } diff -r 8a6697503832 -r 279aeb153688 vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryDataExtractorTest.java --- a/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryDataExtractorTest.java Tue Jan 22 16:14:48 2013 +0100 +++ b/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryDataExtractorTest.java Tue Jan 22 16:15:32 2013 +0100 @@ -44,6 +44,8 @@ import org.junit.Test; +import com.redhat.thermostat.storage.model.VmMemoryStat.Generation; + import sun.jvmstat.monitor.LongMonitor; import sun.jvmstat.monitor.MonitorException; import sun.jvmstat.monitor.MonitoredVm; @@ -136,6 +138,18 @@ } @Test + public void testGenerationCollectorNone() throws MonitorException { + final String MONITOR_NAME = "sun.gc.collector.0.name"; + MonitoredVm vm = mock(MonitoredVm.class); + when(vm.findByName(MONITOR_NAME)).thenReturn(null); + + VmMemoryDataExtractor extractor = new VmMemoryDataExtractor(vm); + String returned = extractor.getGenerationCollector(0); + + assertEquals(Generation.COLLECTOR_NONE, returned); + } + + @Test public void testTotalSpaces() throws MonitorException { final Long TOTAL_SPACES = 99l; final LongMonitor monitor = mock(LongMonitor.class); diff -r 8a6697503832 -r 279aeb153688 vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryHostListenerTest.java --- a/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryHostListenerTest.java Tue Jan 22 16:14:48 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,136 +0,0 @@ -/* - * Copyright 2013 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 - * . - * - * 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.vm.memory.agent.internal; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.net.URISyntaxException; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import org.junit.Before; -import org.junit.Test; - -import sun.jvmstat.monitor.HostIdentifier; -import sun.jvmstat.monitor.MonitorException; -import sun.jvmstat.monitor.MonitoredHost; -import sun.jvmstat.monitor.MonitoredVm; -import sun.jvmstat.monitor.VmIdentifier; -import sun.jvmstat.monitor.event.VmStatusChangeEvent; - -import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO; - -public class VmMemoryHostListenerTest { - - private VmMemoryHostListener hostListener; - private MonitoredHost host; - private MonitoredVm monitoredVm1; - private MonitoredVm monitoredVm2; - - @Before - public void setup() throws MonitorException, URISyntaxException { - VmMemoryStatDAO vmMemoryStatDAO = mock(VmMemoryStatDAO.class); - hostListener = new VmMemoryHostListener(vmMemoryStatDAO, true); - - host = mock(MonitoredHost.class); - HostIdentifier hostId = mock(HostIdentifier.class); - monitoredVm1 = mock(MonitoredVm.class); - monitoredVm2 = mock(MonitoredVm.class); - VmIdentifier vmId1 = new VmIdentifier("1"); - VmIdentifier vmId2 = new VmIdentifier("2"); - when(host.getHostIdentifier()).thenReturn(hostId); - when(host.getMonitoredVm(eq(vmId1))).thenReturn(monitoredVm1); - when(host.getMonitoredVm(eq(vmId2))).thenReturn(monitoredVm2); - when(hostId.resolve(eq(vmId1))).thenReturn(vmId1); - when(hostId.resolve(eq(vmId2))).thenReturn(vmId2); - } - - @Test - public void testNewVM() throws InterruptedException, MonitorException { - startVMs(); - - assertTrue(hostListener.getMonitoredVms().containsKey(1)); - assertTrue(hostListener.getMonitoredVms().containsKey(2)); - assertEquals(monitoredVm1, hostListener.getMonitoredVms().get(1)); - assertEquals(monitoredVm2, hostListener.getMonitoredVms().get(2)); - - assertTrue(hostListener.getRegisteredListeners().containsKey(monitoredVm1)); - assertTrue(hostListener.getRegisteredListeners().containsKey(monitoredVm2)); - } - - @Test - public void testStoppedVM() throws InterruptedException, MonitorException { - final Set stopped = new HashSet<>(); - stopped.add(1); - - startVMs(); - - // Trigger a change event - VmStatusChangeEvent event = mock(VmStatusChangeEvent.class); - when(event.getMonitoredHost()).thenReturn(host); - when(event.getStarted()).thenReturn(Collections.emptySet()); - when(event.getTerminated()).thenReturn(stopped); - hostListener.vmStatusChanged(event); - - // Ensure only 1 removed - assertFalse(hostListener.getMonitoredVms().containsKey(1)); - assertTrue(hostListener.getMonitoredVms().containsKey(2)); - assertEquals(monitoredVm2, hostListener.getMonitoredVms().get(2)); - - assertFalse(hostListener.getRegisteredListeners().containsKey(monitoredVm1)); - assertTrue(hostListener.getRegisteredListeners().containsKey(monitoredVm2)); - } - - private void startVMs() throws InterruptedException, MonitorException { - final Set started = new HashSet<>(); - started.add(1); - started.add(2); - - // Trigger a change event - VmStatusChangeEvent event = mock(VmStatusChangeEvent.class); - when(event.getMonitoredHost()).thenReturn(host); - when(event.getStarted()).thenReturn(started); - when(event.getTerminated()).thenReturn(Collections.emptySet()); - hostListener.vmStatusChanged(event); - } -} diff -r 8a6697503832 -r 279aeb153688 vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryVmListenerTest.java --- a/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryVmListenerTest.java Tue Jan 22 16:14:48 2013 +0100 +++ b/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryVmListenerTest.java Tue Jan 22 16:15:32 2013 +0100 @@ -37,16 +37,21 @@ package com.redhat.thermostat.vm.memory.agent.internal; import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; +import sun.jvmstat.monitor.Monitor; import sun.jvmstat.monitor.MonitorException; import sun.jvmstat.monitor.MonitoredVm; +import sun.jvmstat.monitor.event.VmEvent; import com.redhat.thermostat.storage.model.VmMemoryStat; import com.redhat.thermostat.storage.model.VmMemoryStat.Generation; @@ -77,19 +82,20 @@ }; private VmMemoryVmListener vmListener; - private MonitoredVm monitoredVm; private VmMemoryDataExtractor extractor; private VmMemoryStatDAO vmMemoryStatDAO; + private MonitoredVm monitoredVm; @Before public void setup() throws MonitorException { final int numGens = 2; vmMemoryStatDAO = mock(VmMemoryStatDAO.class); vmListener = new VmMemoryVmListener(vmMemoryStatDAO, 0); - monitoredVm = mock(MonitoredVm.class); extractor = mock(VmMemoryDataExtractor.class); - + + mockTotalGenerations(numGens); + for (int i = 0; i < numGens; i++) { mockGenerationName(i); mockGenerationCapacity(i); @@ -106,6 +112,10 @@ } } + private void mockTotalGenerations(long gens) throws MonitorException { + when(extractor.getTotalGcGenerations()).thenReturn(gens); + } + private void mockGenerationName(int gen) throws MonitorException { when(extractor.getGenerationName(gen)).thenReturn(GEN_NAMES[gen]); } @@ -141,15 +151,43 @@ private void mockSpaceUsed(int gen, int space) throws MonitorException { when(extractor.getSpaceUsed(gen, space)).thenReturn(SPACE_USED[gen][space]); } - + + @Test + public void testDisconnectedIsNoOp() { + vmListener.disconnected(null); + + verifyNoMoreInteractions(vmMemoryStatDAO, extractor); + } + + @Test + public void testMonitorStatusChangeIsNoOp() { + vmListener.monitorStatusChanged(null); + + verifyNoMoreInteractions(vmMemoryStatDAO, extractor); + } + + @Test + public void testMonitorsUpdated() throws MonitorException { + Monitor monitor = mock(Monitor.class); + when(monitor.getValue()).thenReturn(Long.valueOf(0)); + when(monitoredVm.findByName(anyString())).thenReturn(monitor); + VmEvent monitorUpdateEvent = mock(VmEvent.class); + when(monitorUpdateEvent.getMonitoredVm()).thenReturn(monitoredVm); + + vmListener.monitorsUpdated(monitorUpdateEvent); + + verify(vmMemoryStatDAO).putVmMemoryStat(isA(VmMemoryStat.class)); + } + @Test public void testRecordMemoryStat() { - vmListener.recordMemoryStat(monitoredVm, extractor); + vmListener.recordMemoryStat(extractor); ArgumentCaptor captor = ArgumentCaptor.forClass(VmMemoryStat.class); verify(vmMemoryStatDAO).putVmMemoryStat(captor.capture()); VmMemoryStat memoryStat = captor.getValue(); Generation[] gens = memoryStat.getGenerations(); + assertEquals(2, gens.length); for (int i = 0; i < gens.length; i++) { Generation gen = gens[i]; assertEquals(GEN_NAMES[i], gen.getName()); @@ -167,4 +205,12 @@ } } } + + @Test + public void testRecordingMemoryInPresenseOfExtrationErrors() throws MonitorException { + when(extractor.getTotalGcGenerations()).thenThrow(new MonitorException()); + vmListener.recordMemoryStat(extractor); + + verifyNoMoreInteractions(vmMemoryStatDAO); + } }