Mercurial > hg > release > thermostat-1.0
changeset 1289:7a0ff324a833
Fix dead-vm (and other things)
review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-October/008550.html
reviewed-by: omajid
line wrap: on
line diff
--- a/client/core/pom.xml Tue Oct 22 17:48:37 2013 +0200 +++ b/client/core/pom.xml Mon Oct 28 16:27:18 2013 +0100 @@ -125,7 +125,8 @@ com.redhat.thermostat.client.core, com.redhat.thermostat.client.core.progress, com.redhat.thermostat.client.core.controllers, - com.redhat.thermostat.client.core.views + com.redhat.thermostat.client.core.views, + com.redhat.thermostat.client.core.vmlist </Export-Package> <Private-Package> com.redhat.thermostat.client.core.internal
--- a/client/core/src/main/java/com/redhat/thermostat/client/core/NameMatchingRefFilter.java Tue Oct 22 17:48:37 2013 +0200 +++ b/client/core/src/main/java/com/redhat/thermostat/client/core/NameMatchingRefFilter.java Mon Oct 28 16:27:18 2013 +0100 @@ -45,12 +45,12 @@ * A {@link Filter} that checks if the name of a {@link Ref} contains * the supplied character substring. This is case sensitive. */ -public class NameMatchingRefFilter<T extends Ref> implements Filter<T> { +public class NameMatchingRefFilter<T extends Ref> extends Filter<T> { private String pattern; public NameMatchingRefFilter() { - this(null); + this(""); } public NameMatchingRefFilter(String pattern) { @@ -67,7 +67,11 @@ } public void setPattern(String pattern) { + String oldPattern = this.pattern; this.pattern = pattern; + if (!Objects.equals(oldPattern, this.pattern)) { + notify(FilterEvent.FILTER_CHANGED); + } } public String getPattern() {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/core/src/main/java/com/redhat/thermostat/client/core/vmlist/HostFilter.java Mon Oct 28 16:27:18 2013 +0100 @@ -0,0 +1,49 @@ +/* + * Copyright 2012, 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 + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.core.vmlist; + +import com.redhat.thermostat.annotations.ExtensionPoint; +import com.redhat.thermostat.common.Filter; +import com.redhat.thermostat.storage.core.HostRef; + +/** + * Filter that controls if {@link HostRef}erences can be shown in the + * reference tree of this Swing Client. + */ +@ExtensionPoint +public abstract class HostFilter extends Filter<HostRef>{ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/core/src/main/java/com/redhat/thermostat/client/core/vmlist/VMFilter.java Mon Oct 28 16:27:18 2013 +0100 @@ -0,0 +1,50 @@ +/* + * Copyright 2012, 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 + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.core.vmlist; + +import com.redhat.thermostat.annotations.ExtensionPoint; +import com.redhat.thermostat.common.Filter; +import com.redhat.thermostat.storage.core.VmRef; + +/** + * Filter that controls if {@link VmRef}erences can be shown in the + * reference tree of this Swing Client. + */ +@ExtensionPoint +public abstract class VMFilter extends Filter<VmRef> { + +}
--- a/client/core/src/main/java/com/redhat/thermostat/client/ui/MainWindowController.java Tue Oct 22 17:48:37 2013 +0200 +++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/MainWindowController.java Mon Oct 28 16:27:18 2013 +0100 @@ -39,9 +39,6 @@ public interface MainWindowController { - public void setHostVmTreeFilter(String filter); - public void showMainMainWindow(); - }
--- a/client/living-vm-filter/core/src/main/java/com/redhat/thermostat/client/filter/vm/core/LivingVMFilter.java Tue Oct 22 17:48:37 2013 +0200 +++ b/client/living-vm-filter/core/src/main/java/com/redhat/thermostat/client/filter/vm/core/LivingVMFilter.java Mon Oct 28 16:27:18 2013 +0100 @@ -36,12 +36,12 @@ package com.redhat.thermostat.client.filter.vm.core; -import com.redhat.thermostat.common.Filter; +import com.redhat.thermostat.client.core.vmlist.VMFilter; import com.redhat.thermostat.storage.core.VmRef; import com.redhat.thermostat.storage.dao.VmInfoDAO; import com.redhat.thermostat.storage.model.VmInfo; -public class LivingVMFilter implements Filter<VmRef> { +public class LivingVMFilter extends VMFilter { volatile boolean filterActive = true; @@ -61,7 +61,11 @@ } public void setActive(boolean active) { + boolean oldActive = this.filterActive; this.filterActive = active; + if (oldActive != filterActive) { + notify(FilterEvent.FILTER_CHANGED); + } } public boolean isActive() {
--- a/client/living-vm-filter/core/src/main/java/com/redhat/thermostat/client/filter/vm/core/internal/VMFilterActivator.java Tue Oct 22 17:48:37 2013 +0200 +++ b/client/living-vm-filter/core/src/main/java/com/redhat/thermostat/client/filter/vm/core/internal/VMFilterActivator.java Mon Oct 28 16:27:18 2013 +0100 @@ -38,8 +38,6 @@ import java.util.ArrayList; import java.util.Collections; -import java.util.Dictionary; -import java.util.Hashtable; import java.util.Iterator; import java.util.List; @@ -49,41 +47,38 @@ import org.osgi.framework.ServiceRegistration; import org.osgi.util.tracker.ServiceTracker; -import com.redhat.thermostat.common.Filter; +import com.redhat.thermostat.client.core.vmlist.VMFilter; import com.redhat.thermostat.client.filter.vm.core.LivingVMFilter; import com.redhat.thermostat.client.ui.MenuAction; -import com.redhat.thermostat.common.Constants; -import com.redhat.thermostat.storage.core.VmRef; import com.redhat.thermostat.storage.dao.VmInfoDAO; public class VMFilterActivator implements BundleActivator { + @SuppressWarnings("rawtypes") private final List<ServiceRegistration> registeredServices = Collections.synchronizedList(new ArrayList<ServiceRegistration>()); + @SuppressWarnings("rawtypes") ServiceTracker vmInfoDaoTracker; + @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public void start(BundleContext context) throws Exception { vmInfoDaoTracker = new ServiceTracker(context, VmInfoDAO.class.getName(), null) { @Override public Object addingService(ServiceReference reference) { + ServiceRegistration registration = null; + VmInfoDAO dao = (VmInfoDAO) context.getService(reference); LivingVMFilter filter = new LivingVMFilter(dao); + registration = context.registerService(VMFilter.class.getName(), filter, null); LivingVMFilterMenuAction menu = new LivingVMFilterMenuAction(filter); - ServiceRegistration registration = null; - registration = context.registerService(MenuAction.class.getName(), menu, null); registeredServices.add(registration); - Dictionary<String, String> properties = new Hashtable<>(); - properties.put(Constants.GENERIC_SERVICE_CLASSNAME, VmRef.class.getName()); - registration = context.registerService(Filter.class.getName(), filter, properties); - registeredServices.add(registration); - return super.addingService(reference); } @@ -103,6 +98,7 @@ vmInfoDaoTracker.open(); } + @SuppressWarnings("rawtypes") @Override public void stop(BundleContext context) throws Exception { vmInfoDaoTracker.close();
--- a/client/living-vm-filter/core/src/test/java/com/redhat/thermostat/client/filter/vm/core/internal/VMFilterActivatorTest.java Tue Oct 22 17:48:37 2013 +0200 +++ b/client/living-vm-filter/core/src/test/java/com/redhat/thermostat/client/filter/vm/core/internal/VMFilterActivatorTest.java Mon Oct 28 16:27:18 2013 +0100 @@ -44,6 +44,7 @@ import org.osgi.framework.ServiceRegistration; import com.redhat.thermostat.common.Filter; +import com.redhat.thermostat.client.core.vmlist.VMFilter; import com.redhat.thermostat.client.filter.vm.core.LivingVMFilter; import com.redhat.thermostat.client.filter.vm.core.internal.VMFilterActivator; import com.redhat.thermostat.client.ui.MenuAction; @@ -66,7 +67,7 @@ activator.vmInfoDaoTracker.addingService(ref); assertTrue(ctx.isServiceRegistered(MenuAction.class.getName(), LivingVMFilterMenuAction.class)); - assertTrue(ctx.isServiceRegistered(Filter.class.getName(), LivingVMFilter.class)); + assertTrue(ctx.isServiceRegistered(VMFilter.class.getName(), LivingVMFilter.class)); } }
--- a/client/living-vm-filter/swing/src/main/java/com/redhat/thermostat/client/filter/vm/swing/LivingVMDecoratorProvider.java Tue Oct 22 17:48:37 2013 +0200 +++ b/client/living-vm-filter/swing/src/main/java/com/redhat/thermostat/client/filter/vm/swing/LivingVMDecoratorProvider.java Mon Oct 28 16:27:18 2013 +0100 @@ -39,7 +39,6 @@ import java.io.IOException; import com.redhat.thermostat.common.Filter; -import com.redhat.thermostat.client.filter.vm.core.LivingVMFilter; import com.redhat.thermostat.client.swing.IconResource; import com.redhat.thermostat.client.ui.Decorator; import com.redhat.thermostat.client.ui.DecoratorProvider; @@ -75,12 +74,12 @@ } } - private LivingVMFilter decoratorFilter; + private com.redhat.thermostat.client.filter.vm.core.LivingVMFilter decoratorFilter; private LivingVMDecorator decorator; public LivingVMDecoratorProvider(VmInfoDAO dao) { decorator = new LivingVMDecorator(); - decoratorFilter = new LivingVMFilter(dao); + decoratorFilter = new com.redhat.thermostat.client.filter.vm.core.LivingVMFilter(dao); } @Override
--- a/client/living-vm-filter/swing/src/main/java/com/redhat/thermostat/client/filter/vm/swing/VMFilterActivator.java Tue Oct 22 17:48:37 2013 +0200 +++ b/client/living-vm-filter/swing/src/main/java/com/redhat/thermostat/client/filter/vm/swing/VMFilterActivator.java Mon Oct 28 16:27:18 2013 +0100 @@ -56,23 +56,26 @@ public class VMFilterActivator implements BundleActivator { + @SuppressWarnings("rawtypes") private final List<ServiceRegistration> registeredServices = Collections.synchronizedList(new ArrayList<ServiceRegistration>()); + @SuppressWarnings("rawtypes") ServiceTracker vmInfoDaoTracker; + @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public void start(BundleContext context) throws Exception { vmInfoDaoTracker = new ServiceTracker(context, VmInfoDAO.class.getName(), null) { @Override public Object addingService(ServiceReference reference) { + ServiceRegistration registration = null; + VmInfoDAO dao = (VmInfoDAO) context.getService(reference); - + LivingVMDecoratorProvider decorator = new LivingVMDecoratorProvider(dao); DeadVMDecoratorProvider deadDecorator = new DeadVMDecoratorProvider(dao); - ServiceRegistration registration = null; - Dictionary<String, String> decoratorProperties = new Hashtable<>(); decoratorProperties.put(Constants.GENERIC_SERVICE_CLASSNAME, VmRef.class.getName()); @@ -101,6 +104,7 @@ vmInfoDaoTracker.open(); } + @SuppressWarnings("rawtypes") @Override public void stop(BundleContext context) throws Exception { vmInfoDaoTracker.close();
--- a/client/swing/pom.xml Tue Oct 22 17:48:37 2013 +0200 +++ b/client/swing/pom.xml Mon Oct 28 16:27:18 2013 +0100 @@ -169,6 +169,7 @@ com.redhat.thermostat.client.swing.components, com.redhat.thermostat.client.swing.components.models, com.redhat.thermostat.client.swing.components.timeline, + com.redhat.thermostat.client.swing.vmlist, </Export-Package> <Private-Package> com.redhat.thermostat.client.swing.internal,
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/HostFilterRegistry.java Tue Oct 22 17:48:37 2013 +0200 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/HostFilterRegistry.java Mon Oct 28 16:27:18 2013 +0100 @@ -40,19 +40,16 @@ import org.osgi.framework.Constants; import org.osgi.framework.InvalidSyntaxException; -import com.redhat.thermostat.common.Filter; +import com.redhat.thermostat.client.core.vmlist.HostFilter; import com.redhat.thermostat.common.ThermostatExtensionRegistry; -import com.redhat.thermostat.storage.core.HostRef; -class HostFilterRegistry extends ThermostatExtensionRegistry<Filter> { +public class HostFilterRegistry extends ThermostatExtensionRegistry<HostFilter> { private static final String FILTER = "(&(" + Constants.OBJECTCLASS + "=" + - Filter.class.getName() + ")(" + - com.redhat.thermostat.common.Constants.GENERIC_SERVICE_CLASSNAME + "=" + - HostRef.class.getName() + "))"; + HostFilter.class.getName() + "))"; public HostFilterRegistry(BundleContext context) throws InvalidSyntaxException { - super(context, FILTER, Filter.class); + super(context, FILTER, HostFilter.class); } }
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainView.java Tue Oct 22 17:48:37 2013 +0200 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainView.java Mon Oct 28 16:27:18 2013 +0100 @@ -56,10 +56,8 @@ enum Action { VISIBLE, HIDDEN, - HOST_VM_TREE_FILTER, SHOW_AGENT_CONFIG, SHOW_CLIENT_CONFIG, - SWITCH_HISTORY_MODE, SHOW_ABOUT_DIALOG, SHUTDOWN, SHOW_HOST_VM_CONTEXT_MENU,
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindow.java Tue Oct 22 17:48:37 2013 +0200 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindow.java Mon Oct 28 16:27:18 2013 +0100 @@ -295,8 +295,10 @@ DecoratorManager decoratorManager = new DecoratorManager(); - hostTree = new Accordion<>(new HostTreeComponentFactory(decoratorManager)); - hostTreeController = new HostTreeController(hostTree, decoratorManager); + HostTreeComponentFactory hostFactory = new HostTreeComponentFactory(decoratorManager); + hostTree = new Accordion<>(hostFactory); + hostTreeController = new HostTreeController(hostTree, decoratorManager, + hostFactory); navigationPanel.addContent(hostTree); final JPanel collapsedPanel = new JPanel();
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindowControllerImpl.java Tue Oct 22 17:48:37 2013 +0200 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindowControllerImpl.java Mon Oct 28 16:27:18 2013 +0100 @@ -40,7 +40,6 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.logging.Level; import java.util.logging.Logger; @@ -48,9 +47,7 @@ import org.osgi.framework.BundleContext; import org.osgi.framework.InvalidSyntaxException; -import com.redhat.thermostat.common.Filter; import com.redhat.thermostat.client.core.InformationService; -import com.redhat.thermostat.client.core.NameMatchingRefFilter; import com.redhat.thermostat.client.core.progress.ProgressNotifier; import com.redhat.thermostat.client.core.views.AgentInformationDisplayView; import com.redhat.thermostat.client.core.views.AgentInformationViewProvider; @@ -64,12 +61,12 @@ import com.redhat.thermostat.client.swing.internal.osgi.InformationServiceTracker; import com.redhat.thermostat.client.swing.internal.osgi.VMContextActionServiceTracker; import com.redhat.thermostat.client.swing.internal.vmlist.controller.DecoratorProviderExtensionListener; +import com.redhat.thermostat.client.swing.internal.vmlist.controller.FilterManager; import com.redhat.thermostat.client.swing.internal.vmlist.controller.HostTreeController; import com.redhat.thermostat.client.swing.internal.vmlist.controller.HostTreeController.ReferenceSelection; import com.redhat.thermostat.client.ui.AgentInformationDisplayController; import com.redhat.thermostat.client.ui.AgentInformationDisplayModel; import com.redhat.thermostat.client.ui.ClientConfigurationController; -import com.redhat.thermostat.client.ui.DecoratorProvider; import com.redhat.thermostat.client.ui.HostInformationController; import com.redhat.thermostat.client.ui.MainWindowController; import com.redhat.thermostat.client.ui.MenuAction; @@ -78,6 +75,7 @@ import com.redhat.thermostat.client.ui.VmInformationController; import com.redhat.thermostat.common.ActionEvent; import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.AllPassFilter; import com.redhat.thermostat.common.ApplicationInfo; import com.redhat.thermostat.common.ApplicationService; import com.redhat.thermostat.common.MultipleServiceTracker; @@ -102,9 +100,6 @@ private static final Logger logger = LoggingUtils.getLogger(MainWindowControllerImpl.class); - private final CopyOnWriteArrayList<Filter<HostRef>> hostFilters = new CopyOnWriteArrayList<>(); - private final CopyOnWriteArrayList<Filter<VmRef>> vmFilters = new CopyOnWriteArrayList<>(); - private final ApplicationInfo appInfo = new ApplicationInfo(); private ApplicationService appSvc; @@ -162,14 +157,6 @@ } }; - private NameMatchingRefFilter<HostRef> hostFilter; - private NameMatchingRefFilter<VmRef> vmFilter; - private VmFilterRegistry vmFilterRegistry; - private HostFilterRegistry hostFilterRegistry; - - private ActionListener<ThermostatExtensionRegistry.Action> hostFilterListener = new FilterExtensionListener<HostRef>(hostFilters); - private ActionListener<ThermostatExtensionRegistry.Action> vmFilterListener = new FilterExtensionListener<VmRef>(vmFilters); - private HostTreeDecoratorRegistry hostDecoratorRegistry; private VMTreeDecoratorRegistry vmDecoratorRegistry; @@ -181,11 +168,14 @@ actionEvent) { // TODO - //updateView(); + // System.err.println(actionEvent.getPayload()); }; }; private VmInformationControllerProvider vmInfoControllerProvider; + private VmFilterRegistry vmFilterRegistry; + private HostFilterRegistry hostFilterRegistry; + private FilterManager filterManager; public MainWindowControllerImpl(BundleContext context, ApplicationService appSvc, CountDownLatch shutdown) { @@ -195,12 +185,16 @@ MainWindowControllerImpl(final BundleContext context, ApplicationService appSvc, final MainView view, RegistryFactory registryFactory, - final CountDownLatch shutdown) { + final CountDownLatch shutdown) + { this.appSvc = appSvc; this.view = view; + try { + vmFilterRegistry = registryFactory.createVmFilterRegistry(); hostFilterRegistry = registryFactory.createHostFilterRegistry(); + hostDecoratorRegistry = registryFactory.createHostTreeDecoratorRegistry(); vmDecoratorRegistry = registryFactory.createVMTreeDecoratorRegistry(); menuRegistry = registryFactory.createMenuRegistry(); @@ -209,13 +203,7 @@ } catch (InvalidSyntaxException e) { throw new RuntimeException(e); } - - hostFilter = new NameMatchingRefFilter<>(); - vmFilter = new NameMatchingRefFilter<>(); - - hostFilters.add(hostFilter); - vmFilters.add(vmFilter); - + this.infoServiceTracker = new InformationServiceTracker(context); this.infoServiceTracker.open(); @@ -272,12 +260,12 @@ initView(); vmInfoControllerProvider = new VmInformationControllerProvider(); - + + installListenersAndStartRegistries(); + vmMonitor = initMonitors(); vmMonitor.start(); - installListenersAndStartRegistries(); - registerProgressNotificator(context); } @@ -299,19 +287,6 @@ return vmMonitor; } - /* - * This method is for testing purposes only - */ - Filter<HostRef> getHostFilter() { - return hostFilter; - } - /* - * This also is for testing purposes. - */ - Filter<VmRef> getVmFilter() { - return vmFilter; - } - /** * This method is for testing purposes only */ @@ -319,15 +294,27 @@ return menuListener; } - @Override - public void setHostVmTreeFilter(String filter) { - this.hostFilter.setPattern(filter); - this.vmFilter.setPattern(filter); + private void initHostVMTree() { + HostTreeController hostController = view.getHostTreeController(); + + // initially fill out with all known host and vms + List<HostRef> hosts = networkMonitor.getHosts(new AllPassFilter<HostRef>()); + AllPassFilter<VmRef> vmFilter = new AllPassFilter<>(); + for (HostRef host : hosts) { + hostController.registerHost(host); + + // get the vm for this host + List<VmRef> vms = hostMonitor.getVirtualMachines(host, vmFilter); + for (VmRef vm : vms) { + hostController.registerVM(vm); + } + } } + + private void initView() { + view.setWindowTitle(appInfo.getName()); - private void initView() { - view.setWindowTitle(appInfo.getName()); - + initHostVMTree(); view.getHostTreeController().addReferenceSelectionChangeListener(new ActionListener<HostTreeController.ReferenceSelection>() { @Override @@ -347,21 +334,12 @@ case VISIBLE: break; - case HOST_VM_TREE_FILTER: - // TODO -// String filter = view.getHostVmTreeFilterText(); -// setHostVmTreeFilter(filter); - break; case SHOW_AGENT_CONFIG: showAgentConfiguration(); break; case SHOW_CLIENT_CONFIG: showConfigureClientPreferences(); break; - case SWITCH_HISTORY_MODE: - // TODO - //switchHistoryMode(); - break; case SHOW_ABOUT_DIALOG: showAboutDialog(); break; @@ -375,6 +353,7 @@ // Main will call shutdownApplication shutdown.countDown(); break; + default: throw new IllegalStateException("unhandled action"); } @@ -403,13 +382,11 @@ menuRegistry.addActionListener(menuListener); menuRegistry.start(); - hostFilterRegistry.addActionListener(hostFilterListener); - hostFilterRegistry.start(); + HostTreeController hostTreeController = view.getHostTreeController(); + filterManager = new FilterManager(vmFilterRegistry, hostFilterRegistry, + hostTreeController); - vmFilterRegistry.addActionListener(vmFilterListener); - vmFilterRegistry.start(); - - HostTreeController hostTreeController = view.getHostTreeController(); + filterManager.start(); DecoratorProviderExtensionListener<HostRef> hostDecoratorListener = hostTreeController.getHostDecoratorListener(); @@ -435,14 +412,8 @@ menuListener = null; menuRegistry.stop(); - hostFilterRegistry.removeActionListener(hostFilterListener); - hostFilterListener = null; - hostFilterRegistry.stop(); - - vmFilterRegistry.removeActionListener(vmFilterListener); - vmFilterListener = null; - vmFilterRegistry.stop(); - + filterManager.stop(); + HostTreeController hostTreeController = view.getHostTreeController(); DecoratorProviderExtensionListener<HostRef> hostDecoratorListener = @@ -559,48 +530,6 @@ } } - private class FilterExtensionListener<T> implements ActionListener<ThermostatExtensionRegistry.Action> { - - private final CopyOnWriteArrayList<Filter<T>> extensionList; - - public FilterExtensionListener(CopyOnWriteArrayList<Filter<T>> addRemoveExtensionsFrom) { - this.extensionList = addRemoveExtensionsFrom; - } - - @SuppressWarnings("unchecked") - @Override - public void actionPerformed(ActionEvent<ThermostatExtensionRegistry.Action> actionEvent) { - - Object payload = actionEvent.getPayload(); - Filter<T> filter = null; - - try { - filter = (Filter<T>) payload; - } catch (ClassCastException cce) { - throw new IllegalArgumentException("unexpected payload type. " + - payload.getClass().getName() + " not allowed here.", cce); - } - - - switch (actionEvent.getActionId()) { - case SERVICE_ADDED: - extensionList.add(filter); - //doUpdateTreeAsync(); - break; - - case SERVICE_REMOVED: - extensionList.remove(filter); - //doUpdateTreeAsync(); - break; - - default: - logger.log(Level.WARNING, "received unknown event from ExtensionRegistry: " + - actionEvent.getActionId()); - break; - } - } - } - private class VmInformationControllerProvider { private VmInformationController lastSelectedVM; private Map<VmRef, Integer> selectedForVM = new ConcurrentHashMap<>();
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/VMMonitorController.java Tue Oct 22 17:48:37 2013 +0200 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/VMMonitorController.java Mon Oct 28 16:27:18 2013 +0100 @@ -72,12 +72,12 @@ HostRef host = (HostRef) actionEvent.getPayload(); switch (actionEvent.getActionId()) { case HOST_ADDED: - view.getHostTreeController().addHost(host); + view.getHostTreeController().registerHost(host); hostMonitor.addHostChangeListener(host, hostListener); break; case HOST_REMOVED: - view.getHostTreeController().removeHost(host); + view.getHostTreeController().updateHostStatus(host); hostMonitor.removeHostChangeListener(host, hostListener); break; @@ -95,11 +95,11 @@ VmRef vm = (VmRef) actionEvent.getPayload(); switch (actionEvent.getActionId()) { case VM_ADDED: - view.getHostTreeController().addVM(vm); + view.getHostTreeController().registerVM(vm); break; case VM_REMOVED: - view.getHostTreeController().removeVM(vm); + view.getHostTreeController().updateVMStatus(vm); default: break;
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/VmFilterRegistry.java Tue Oct 22 17:48:37 2013 +0200 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/VmFilterRegistry.java Mon Oct 28 16:27:18 2013 +0100 @@ -40,19 +40,15 @@ import org.osgi.framework.Constants; import org.osgi.framework.InvalidSyntaxException; -import com.redhat.thermostat.common.Filter; +import com.redhat.thermostat.client.core.vmlist.VMFilter; import com.redhat.thermostat.common.ThermostatExtensionRegistry; -import com.redhat.thermostat.storage.core.VmRef; -class VmFilterRegistry extends ThermostatExtensionRegistry<Filter> { +public class VmFilterRegistry extends ThermostatExtensionRegistry<VMFilter> { private static final String FILTER = "(&(" + Constants.OBJECTCLASS + "=" + - Filter.class.getName() + ")(" + - com.redhat.thermostat.common.Constants.GENERIC_SERVICE_CLASSNAME + "=" + - VmRef.class.getName() + "))"; + VMFilter.class.getName() + "))"; public VmFilterRegistry(BundleContext context) throws InvalidSyntaxException { - super(context, FILTER, Filter.class); + super(context, FILTER, VMFilter.class); } } -
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/accordion/Accordion.java Tue Oct 22 17:48:37 2013 +0200 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/accordion/Accordion.java Mon Oct 28 16:27:18 2013 +0100 @@ -204,6 +204,8 @@ JComponent content = pane.getContent(); content.remove(contentUnit); content.revalidate(); + + Accordion.this.contentPane.revalidate(); } @Override @@ -224,4 +226,23 @@ public void removeAccordionItemSelectedChangeListener(AccordionItemSelectedChangeListener l) { componentController.removeAccordionItemSelectedChangeListener(l); } + + public AccordionComponent getSelectedComponent() { + return componentController.getSelectedComponent(); + } + + public void setSelectedComponent(AccordionComponent component) { + componentController.setSelectedItem(component); + } + + public boolean isExpanded(H header) { + return !panes.containsKey(header) || panes.get(header).isExpanded(); + } + + public void setExpanded(H header, boolean expanded) { + if (panes.containsKey(header)) { + panes.get(header).setExpanded(expanded); + repaint(); + } + } }
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/accordion/AccordionComponentController.java Tue Oct 22 17:48:37 2013 +0200 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/accordion/AccordionComponentController.java Mon Oct 28 16:27:18 2013 +0100 @@ -83,4 +83,8 @@ } } } + + AccordionComponent getSelectedComponent() { + return selectedComponent; + } }
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/accordion/AccordionModel.java Tue Oct 22 17:48:37 2013 +0200 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/accordion/AccordionModel.java Mon Oct 28 16:27:18 2013 +0100 @@ -38,7 +38,9 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Set; import javax.swing.event.EventListenerList; @@ -50,23 +52,29 @@ * {@link AccordionComponentFactory} is responsible to create the specific * widget to be used inside the Accordion. * + * <br /><br /> + * * <strong>Note</strong>: H and C must be usable as hash keys. + * + * <br /> + * + * <strong>Note</strong>: This class is <strong>not</strong> thread safe. */ public class AccordionModel<H, C> { protected EventListenerList listenerList; - private HashMap<H, List<C>> components; + private HashMap<H, Set<C>> components; public AccordionModel() { components = new HashMap<>(); listenerList = new EventListenerList(); } - private List<C> addOrGetHeader(H header) { - List<C> _components = components.get(header); + private Set<C> addOrGetHeader(H header) { + Set<C> _components = components.get(header); if (_components == null) { - _components = new ArrayList<>(); + _components = new HashSet<>(); components.put(header, _components); fireHeaderAddedEvent(header); @@ -74,14 +82,62 @@ return _components; } + /** + * Gets a {@link List} representation of all the headers currently + * held by this model. The {@link List} can be modified, however the + * headers are references to the actual headers contained in this model. + * + * <br /><br /> + * + * If this model is currently empty, an emtpy {@link List} is returned. + */ + public List<H> getHeaders() { + List<H> result = new ArrayList<>(); + if (components.size() != 0) { + for (H header : components.keySet()) { + result.add(header); + } + } + return result; + } + + /** + * Gets a {@link List} representation of all the components currently + * held by this model and relative to the passed header. The {@link List} + * can be modified, however the components are references to the actual + * components contained in this model. + * + * <br /><br /> + * + * If no components exist of the given header, an emtpy {@link List} is + * returned. + */ + public List<C> getComponents(H header) { + List<C> result = new ArrayList<>(); + if (components.containsKey(header)) { + Set<C> componentSet = components.get(header); + for (C component : componentSet) { + result.add(component); + } + } + return result; + } + + /** + * Adds this header to this model. If the header already exist in this + * model, this is a no-op. + */ public boolean addHeader(H header) { boolean result = components.containsKey(header); addOrGetHeader(header); return result; } + /** + * Removes this header from this model. + */ public boolean removeHeader(H header) { - List<C> _components = components.remove(header); + Set<C> _components = components.remove(header); boolean result = (_components != null); if (result) { for (C component :_components) { @@ -92,15 +148,27 @@ return result; } + /** + * Adds this component to the given header. If the header is not in the + * model already, it is also added. + */ public boolean addComponent(H header, C component) { - List<C> _components = addOrGetHeader(header); - boolean result = _components.add(component); - fireComponentAddedEvent(header, component); + Set<C> _components = addOrGetHeader(header); + boolean result = false; + if (!_components.contains(component)) { + result = _components.add(component); + fireComponentAddedEvent(header, component); + } return result; } + /** + * Removes the current component from the given header. If the header + * is not contained in this model, or the component does not belong to + * the passed header, this is a no-op. + */ public boolean removeComponent(H header, C component) { - List<C> _components = components.get(header); + Set<C> _components = components.get(header); boolean result = false; if (_components != null) { result = _components.remove(component); @@ -109,22 +177,42 @@ return result; } + /** + * Returns the total number of header objects contained in this model, + * not including their components. + */ public int headerSize() { return components.size(); } + /** + * Returns the total number of objects contained in this model. The total + * size is the sum of {@link #headerSize()} plus the number of components + * contained under each header. + */ public int size() { int size = components.size(); - for (List<C> comp : components.values()) { + for (Set<C> comp : components.values()) { size += comp.size(); } return size; } - public void addAccordionModelChangeListener(AccordionModelChangeListener l) { - listenerList.add(AccordionModelChangeListener.class, l); + /** + * Adds an {@link AccordionModelChangeListener} listener to this model. + */ + public void addAccordionModelChangeListener(AccordionModelChangeListener<H, C> listener) { + listenerList.add(AccordionModelChangeListener.class, listener); + } + + /** + * Removes this {@link AccordionModelChangeListener} listener from this model. + */ + public void removeAccordionModelChangeListener(AccordionModelChangeListener<H, C> listener) { + listenerList.remove(AccordionModelChangeListener.class, listener); } + @SuppressWarnings({ "unchecked", "rawtypes" }) private void fireHeaderRemovedEvent(H header) { Object[] listeners = listenerList.getListenerList(); @@ -137,6 +225,7 @@ } } + @SuppressWarnings({ "unchecked", "rawtypes" }) private void fireHeaderAddedEvent(H header) { Object[] listeners = listenerList.getListenerList(); @@ -149,6 +238,7 @@ } } + @SuppressWarnings({ "unchecked", "rawtypes" }) private void fireComponentAddedEvent(H header, C component) { Object[] listeners = listenerList.getListenerList(); @@ -161,6 +251,7 @@ } } + @SuppressWarnings({ "unchecked", "rawtypes" }) private void fireComponentRemovedEvent(H header, C component) { Object[] listeners = listenerList.getListenerList(); @@ -172,4 +263,15 @@ } } } + + /** + * Clears this accordion model. This will result in the removal of all the + * headers (and subsequently of the respective components) in this model, + * generating + */ + public void clear() { + for (H header : getHeaders()) { + removeHeader(header); + } + } }
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/vmlist/HostTreeComponentFactory.java Tue Oct 22 17:48:37 2013 +0200 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/vmlist/HostTreeComponentFactory.java Mon Oct 28 16:27:18 2013 +0100 @@ -36,6 +36,9 @@ package com.redhat.thermostat.client.swing.internal.vmlist; +import java.util.HashMap; +import java.util.Map; + import com.redhat.thermostat.client.swing.internal.accordion.AccordionComponent; import com.redhat.thermostat.client.swing.internal.accordion.AccordionComponentFactory; import com.redhat.thermostat.client.swing.internal.accordion.TitledPane; @@ -45,16 +48,23 @@ public class HostTreeComponentFactory implements AccordionComponentFactory<HostRef, VmRef>{ + private Map<VmRef, AccordionComponent> components; + private Map<HostRef, ReferenceTitle> headers; + private DecoratorManager decoratorManager; public HostTreeComponentFactory(DecoratorManager decoratorManager) { this.decoratorManager = decoratorManager; + components = new HashMap<>(); + headers = new HashMap<>(); } @Override public TitledPane createHeader(HostRef header) { ReferenceTitle pane = new ReferenceTitle(header); decoratorManager.registerAndSetIcon(pane); + headers.put(header, pane); + return pane; } @@ -62,6 +72,16 @@ public AccordionComponent createComponent(HostRef header, VmRef component) { ReferenceComponent refComponent = new ReferenceComponent(component); decoratorManager.registerAndSetIcon(refComponent); + components.put(component, refComponent); + return refComponent; } + + public AccordionComponent getAccordionComponent(VmRef vm) { + return components.get(vm); + } + + public ReferenceTitle getTitledPane(HostRef host) { + return headers.get(host); + } }
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/vmlist/ReferenceComponent.java Tue Oct 22 17:48:37 2013 +0200 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/vmlist/ReferenceComponent.java Mon Oct 28 16:27:18 2013 +0100 @@ -43,7 +43,6 @@ import java.awt.Graphics2D; import javax.swing.Box; -import javax.swing.JLabel; import javax.swing.JPanel; import com.redhat.thermostat.client.swing.components.CompositeIcon;
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/vmlist/controller/DecoratorProviderExtensionListener.java Tue Oct 22 17:48:37 2013 +0200 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/vmlist/controller/DecoratorProviderExtensionListener.java Mon Oct 28 16:27:18 2013 +0100 @@ -58,6 +58,7 @@ public enum Action { DECORATOR_ADDED, DECORATOR_REMOVED, + DECORATION_CHANGED, } private final ActionNotifier<Action> decoratorChangeNotifier; @@ -112,4 +113,8 @@ CopyOnWriteArrayList<DecoratorProvider<T>> getDecorators() { return decorators; } + + public void decorationChanged() { + decoratorChangeNotifier.fireAction(Action.DECORATION_CHANGED); + } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/vmlist/controller/FilterManager.java Mon Oct 28 16:27:18 2013 +0100 @@ -0,0 +1,103 @@ +/* + * Copyright 2012, 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 + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.internal.vmlist.controller; + +import com.redhat.thermostat.client.core.vmlist.HostFilter; +import com.redhat.thermostat.client.core.vmlist.VMFilter; +import com.redhat.thermostat.client.swing.internal.HostFilterRegistry; +import com.redhat.thermostat.client.swing.internal.VmFilterRegistry; +import com.redhat.thermostat.common.ActionEvent; +import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.ThermostatExtensionRegistry; +import com.redhat.thermostat.common.ThermostatExtensionRegistry.Action; + +public class FilterManager { + + private ActionListener<ThermostatExtensionRegistry.Action> hostFilterListener; + private ActionListener<ThermostatExtensionRegistry.Action> vmFilterListener; + + private VmFilterRegistry vmFilterRegistry; + private HostFilterRegistry hostFilterRegistry; + + public FilterManager(VmFilterRegistry vmFilterRegistry, + HostFilterRegistry hostFilterRegistry, + final HostTreeController hostController) + { + + hostFilterListener = new ActionListener<ThermostatExtensionRegistry.Action>() { + @Override + public void actionPerformed(ActionEvent<Action> actionEvent) { + if (actionEvent.getActionId() == Action.SERVICE_ADDED){ + hostController.addHostFilter((HostFilter) actionEvent.getPayload()); + } else { + hostController.removeHostFilter((HostFilter) actionEvent.getPayload()); + } + } + }; + vmFilterListener = new ActionListener<ThermostatExtensionRegistry.Action>() { + @Override + public void actionPerformed(ActionEvent<Action> actionEvent) { + if (actionEvent.getActionId() == Action.SERVICE_ADDED){ + hostController.addVMFilter((VMFilter) actionEvent.getPayload()); + } else { + hostController.removeVMFilter((VMFilter) actionEvent.getPayload()); + } + } + }; + + this.vmFilterRegistry = vmFilterRegistry; + this.hostFilterRegistry = hostFilterRegistry; + } + + public void start() { + hostFilterRegistry.addActionListener(hostFilterListener); + hostFilterRegistry.start(); + + vmFilterRegistry.addActionListener(vmFilterListener); + vmFilterRegistry.start(); + } + + public void stop() { + hostFilterRegistry.removeActionListener(hostFilterListener); + hostFilterListener = null; + hostFilterRegistry.stop(); + + vmFilterRegistry.removeActionListener(vmFilterListener); + vmFilterListener = null; + vmFilterRegistry.stop(); + } +}
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/vmlist/controller/HostTreeController.java Tue Oct 22 17:48:37 2013 +0200 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/vmlist/controller/HostTreeController.java Mon Oct 28 16:27:18 2013 +0100 @@ -36,16 +36,30 @@ package com.redhat.thermostat.client.swing.internal.vmlist.controller; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + import javax.swing.SwingUtilities; +import com.redhat.thermostat.client.core.vmlist.HostFilter; +import com.redhat.thermostat.client.core.vmlist.VMFilter; import com.redhat.thermostat.client.swing.internal.accordion.Accordion; +import com.redhat.thermostat.client.swing.internal.accordion.AccordionComponent; +import com.redhat.thermostat.client.swing.internal.accordion.AccordionComponentEvent; +import com.redhat.thermostat.client.swing.internal.accordion.AccordionHeaderEvent; import com.redhat.thermostat.client.swing.internal.accordion.AccordionItemSelectedChangeListener; import com.redhat.thermostat.client.swing.internal.accordion.AccordionModel; +import com.redhat.thermostat.client.swing.internal.accordion.AccordionModelChangeListener; import com.redhat.thermostat.client.swing.internal.accordion.ItemSelectedEvent; +import com.redhat.thermostat.client.swing.internal.vmlist.HostTreeComponentFactory; import com.redhat.thermostat.client.swing.internal.vmlist.ReferenceProvider; +import com.redhat.thermostat.common.ActionEvent; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.ActionNotifier; +import com.redhat.thermostat.common.Filter; +import com.redhat.thermostat.common.Filter.FilterEvent; import com.redhat.thermostat.storage.core.HostRef; +import com.redhat.thermostat.storage.core.Ref; import com.redhat.thermostat.storage.core.VmRef; public class HostTreeController { @@ -55,15 +69,61 @@ } private DecoratorManager decoratorManager; - + private HostTreeComponentFactory componentFactory; + private final ActionNotifier<ReferenceSelection> referenceNotifier; - private AccordionModel<HostRef, VmRef> model; + private CopyOnWriteArrayList<Filter<HostRef>> hostFilters; + private CopyOnWriteArrayList<Filter<VmRef>> vmFilters; + + private FilterListener filterListener; + + private Accordion<HostRef, VmRef> accordion; - public HostTreeController(Accordion<HostRef, VmRef> accordion, DecoratorManager decoratorManager) { + // we keep a private fullModel updated with all the references, while we + // apply filters and decorations to the accordion original model only + private AccordionModel<HostRef, VmRef> proxyModel; + private AccordionModel<HostRef, VmRef> fullModel; + private class AccordionModelProxy implements AccordionModelChangeListener<HostRef, VmRef> { + @Override + public synchronized void headerAdded(AccordionHeaderEvent<HostRef> e) { + proxyModel.addHeader(e.getHeader()); + } + + @Override + public synchronized void headerRemoved(AccordionHeaderEvent<HostRef> e) { + proxyModel.removeHeader(e.getHeader()); + } + + @Override + public synchronized void componentAdded(AccordionComponentEvent<HostRef, VmRef> e) { + proxyModel.addComponent(e.getHeader(), e.getComponent()); + } + + @Override + public synchronized void componentRemoved(AccordionComponentEvent<HostRef, VmRef> e) { + proxyModel.removeComponent(e.getHeader(), e.getComponent()); + } + } + + public HostTreeController(Accordion<HostRef, VmRef> accordion, + DecoratorManager decoratorManager, + HostTreeComponentFactory componentFactory) + { + this.accordion = accordion; + this.componentFactory = componentFactory; + + filterListener = new FilterListener(); + + hostFilters = new CopyOnWriteArrayList<>(); + vmFilters = new CopyOnWriteArrayList<>(); + + fullModel = new AccordionModel<>(); + fullModel.addAccordionModelChangeListener(new AccordionModelProxy()); + this.decoratorManager = decoratorManager; referenceNotifier = new ActionNotifier<>(this); - this.model = accordion.getModel(); + this.proxyModel = accordion.getModel(); accordion.addAccordionItemSelectedChangeListener(new AccordionItemSelectedChangeListener() { @Override public void itemSelected(ItemSelectedEvent event) { @@ -81,38 +141,129 @@ referenceNotifier.removeActionListener(listener); } - public void addHost(final HostRef host) { + public synchronized void registerHost(final HostRef host) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + fullModel.addHeader(host); + if (filter(hostFilters, host)) { + proxyModel.removeHeader(host); + } + } + }); + } + + public synchronized void updateHostStatus(final HostRef host) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - model.addHeader(host); + if (filter(hostFilters, host)) { + proxyModel.removeHeader(host); + } + decoratorManager.getHostDecoratorListener().decorationChanged(); + } + }); + } + + private void addHostToProxy(final HostRef host) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + proxyModel.addHeader(host); + } + }); + } + + private void setAccordionExpanded(final HostRef ref, final boolean expanded) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + accordion.setExpanded(ref, expanded); + } + }); + } + + private void addVmToProxy(final VmRef vm) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + proxyModel.addComponent(vm.getHostRef(), vm); } }); } - public void removeHost(final HostRef host) { + private void addVMImpl(final VmRef vm) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - model.removeHeader(host); + fullModel.addComponent(vm.getHostRef(), vm); + if (filter(vmFilters, vm)) { + proxyModel.removeComponent(vm.getHostRef(), vm); + } + } + }); + } + + private <R> boolean filter(CopyOnWriteArrayList<Filter<R>> filters, R ref) { + for (Filter<R> filter : filters) { + if (!filter.matches(ref)) { + return true; + } + } + return false; + } + + public synchronized void registerVM(final VmRef vm) { + addVMImpl(vm); + } + + private AccordionComponent getHostComponent(HostRef reference) { + AccordionComponent component = null; + List<HostRef> hosts = proxyModel.getHeaders(); + if (hosts.contains(reference)) { + component = componentFactory.getTitledPane(reference); + } + return component; + } + + private void selectComponent(final Ref _selected) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + Ref selected = _selected; + AccordionComponent component = null; + if (selected instanceof VmRef) { + VmRef reference = (VmRef) selected; + List<VmRef> vms = proxyModel.getComponents(reference.getHostRef()); + if (vms.contains(reference)) { + component = componentFactory.getAccordionComponent(reference); + } else { + // try select the host relative to this vm then, since + // the vm has been removed + selected = reference.getHostRef(); + } + } + + if (selected instanceof HostRef) { + component = getHostComponent((HostRef) selected); + } + + // if this is not the case, let's just not select anything + if (component != null) { + accordion.setSelectedComponent(component); + } } }); } - public void addVM(final VmRef vm) { + public synchronized void updateVMStatus(final VmRef vm) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - model.addComponent(vm.getHostRef(), vm); - } - }); - } - - public void removeVM(final VmRef vm) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - model.removeComponent(vm.getHostRef(), vm); + if (filter(vmFilters, vm)) { + proxyModel.removeComponent(vm.getHostRef(), vm); + } + decoratorManager.getVmDecoratorListener().decorationChanged(); } }); } @@ -124,4 +275,97 @@ public DecoratorProviderExtensionListener<VmRef> getVmDecoratorListener() { return decoratorManager.getVmDecoratorListener(); } + + public void addHostFilter(HostFilter filter) { + hostFilters.add(filter); + filter.addFilterEventListener(filterListener); + rebuildTree(); + } + + public void removeHostFilter(HostFilter filter) { + hostFilters.remove(filter); + filter.removeFilterEventListener(filterListener); + rebuildTree(); + } + + public void addVMFilter(VMFilter filter) { + vmFilters.add(filter); + filter.addFilterEventListener(filterListener); + rebuildTree(); + } + + public void removeVMFilter(VMFilter filter) { + vmFilters.remove(filter); + filter.removeFilterEventListener(filterListener); + rebuildTree(); + } + + private synchronized void rebuildTree() { + Ref selected = null; + AccordionComponent component = accordion.getSelectedComponent(); + if (component instanceof ReferenceProvider) { + // we know this is the case, because we gave the factory + // in the first place, but anyway... + selected = ((ReferenceProvider) component).getReference(); + } + + // operate on a copy first since we need to know which of the ones we + // have now was previously collapsed/expanded, we can't do this + // if we empty the model first + AccordionModel<RefPayload<HostRef>, RefPayload<VmRef>> _model = new AccordionModel<>(); + List<HostRef> hosts = fullModel.getHeaders(); + for (HostRef host : hosts) { + if (!filter(hostFilters, host)) { + + RefPayload<HostRef> hostPayload = new RefPayload<>(); + hostPayload.reference = host; + hostPayload.expanded = accordion.isExpanded(host); + + _model.addHeader(hostPayload); + List<VmRef> vms = fullModel.getComponents(host); + for (VmRef vm : vms) { + if (!filter(vmFilters, vm)) { + + RefPayload<VmRef> vmPayload = new RefPayload<>(); + vmPayload.reference = vm; + _model.addComponent(hostPayload, vmPayload); + } + } + } + } + + // clear and refill, then expand as appropriate + proxyModel.clear(); + List<RefPayload<HostRef>> payloadsHosts = _model.getHeaders(); + for (RefPayload<HostRef> host : payloadsHosts) { + addHostToProxy(host.reference); + + List<RefPayload<VmRef>> vms = _model.getComponents(host); + for (RefPayload<VmRef> vm : vms) { + addVmToProxy(vm.reference); + } + + // need to do this after we add content to the host + // or it won't take effect + setAccordionExpanded(host.reference, host.expanded); + } + + // now select the entry that was originally selected, or its parent... + // ... or nothing + if (selected != null) { + selectComponent(selected); + } + } + + private class FilterListener implements ActionListener<Filter.FilterEvent> { + @Override + public void actionPerformed(ActionEvent<FilterEvent> actionEvent) { + rebuildTree(); + } + } + + private class RefPayload<R extends Ref> { + R reference; + boolean expanded; + } } \ No newline at end of file
--- a/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/MainWindowControllerImplTest.java Tue Oct 22 17:48:37 2013 +0200 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/MainWindowControllerImplTest.java Mon Oct 28 16:27:18 2013 +0100 @@ -315,23 +315,23 @@ verify(view).showMainWindow(); } - @Test - public void verifyUpdateHostsVMsLoadsCorrectVMWithFilter() { - - VmRef ref1 = mock(VmRef.class); - when(ref1.getStringID()).thenReturn("test1"); - when(ref1.getName()).thenReturn("test1"); - - VmRef ref2 = mock(VmRef.class); - when(ref2.getStringID()).thenReturn("test2"); - when(ref2.getName()).thenReturn("test2"); - - controller.setHostVmTreeFilter("test1"); - - Filter<VmRef> filter = controller.getVmFilter(); - assertTrue(filter.matches(ref1)); - assertFalse(filter.matches(ref2)); - } +// @Test +// public void verifyUpdateHostsVMsLoadsCorrectVMWithFilter() { +// +// VmRef ref1 = mock(VmRef.class); +// when(ref1.getStringID()).thenReturn("test1"); +// when(ref1.getName()).thenReturn("test1"); +// +// VmRef ref2 = mock(VmRef.class); +// when(ref2.getStringID()).thenReturn("test2"); +// when(ref2.getName()).thenReturn("test2"); +// +// controller.setHostVmTreeFilter("test1"); +// +// Filter<VmRef> filter = controller.getVmFilter(); +// assertTrue(filter.matches(ref1)); +// assertFalse(filter.matches(ref2)); +// } // private void assertEqualCollection(Collection<?> expected, Collection<?> actual) { // assertEquals(expected.size(), actual.size());
--- a/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/MainWindowTest.java Tue Oct 22 17:48:37 2013 +0200 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/MainWindowTest.java Mon Oct 28 16:27:18 2013 +0100 @@ -36,10 +36,8 @@ package com.redhat.thermostat.client.swing.internal; -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; @@ -48,7 +46,6 @@ 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; @@ -65,25 +62,19 @@ import org.fest.swing.exception.ComponentLookupException; import org.fest.swing.fixture.FrameFixture; import org.fest.swing.fixture.JMenuItemFixture; -import org.fest.swing.fixture.JTextComponentFixture; -import org.fest.swing.fixture.JTreeFixture; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import com.redhat.thermostat.common.Filter; -import com.redhat.thermostat.client.swing.components.SearchField; -import com.redhat.thermostat.client.ui.ContextAction; import com.redhat.thermostat.client.ui.Decorator; import com.redhat.thermostat.client.ui.DecoratorProvider; -import com.redhat.thermostat.client.ui.HostContextAction; import com.redhat.thermostat.client.ui.MenuAction; import com.redhat.thermostat.common.ActionEvent; import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.Filter; import com.redhat.thermostat.shared.locale.LocalizedString; import com.redhat.thermostat.storage.core.HostRef; import com.redhat.thermostat.storage.core.HostsVMsLoader; @@ -163,17 +154,6 @@ verify(decorator, atLeastOnce()).getLabel("fluffhost2"); } -// @Category(GUITest.class) -// @Test -// public void testHostVMTreeFilterPropertySupport() { -// String SEARCH_TEXT = "test"; -// frameFixture.show(); -// JTextComponentFixture hostVMTreeFilterField = frameFixture.textBox(SearchField.VIEW_NAME); -// hostVMTreeFilterField.enterText(SEARCH_TEXT); -// -// verify(l, times(SEARCH_TEXT.length())).actionPerformed(new ActionEvent<MainView.Action>(window, MainView.Action.HOST_VM_TREE_FILTER)); -// } - @Category(GUITest.class) @Test public void verifyThatCloseFiresShutdownEvent() { @@ -235,20 +215,6 @@ verify(l).actionPerformed(new ActionEvent<MainView.Action>(window, MainView.Action.SHOW_AGENT_CONFIG)); } - - // FIXME: re-add when history mode is back - //@Category(GUITest.class) - //@Test - public void verifyThatHistorySwitchTriggersEvent() { - frameFixture.show(); - JMenuItemFixture menuItem = frameFixture.menuItem("historyModeSwitch"); - menuItem.click(); - frameFixture.close(); - frameFixture.requireNotVisible(); - - verify(l).actionPerformed(new ActionEvent<MainView.Action>(window, MainView.Action.SWITCH_HISTORY_MODE)); - } - @Category(GUITest.class) @Test public void addRemoveMenu() {
--- a/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/VMMonitorControllerTest.java Tue Oct 22 17:48:37 2013 +0200 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/VMMonitorControllerTest.java Mon Oct 28 16:27:18 2013 +0100 @@ -95,13 +95,13 @@ networkListener.actionPerformed(event); - verify(treeController).addHost(host1); + verify(treeController).registerHost(host1); event = new ActionEvent<NetworkMonitor.Action>(networkMonitor, Action.HOST_REMOVED); event.setPayload(host1); networkListener.actionPerformed(event); - verify(treeController).removeHost(host1); + verify(treeController).updateHostStatus(host1); } }
--- a/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/accordion/AccordionModelTest.java Tue Oct 22 17:48:37 2013 +0200 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/accordion/AccordionModelTest.java Mon Oct 28 16:27:18 2013 +0100 @@ -36,13 +36,92 @@ package com.redhat.thermostat.client.swing.internal.accordion; -import static org.junit.Assert.*; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.util.List; import org.junit.Test; public class AccordionModelTest { @Test + public void testAccessHeader() { + AccordionModel<String, String> testModel = new AccordionModel<>(); + testModel.addComponent("test0", "testComponent0"); + testModel.addComponent("test0", "testComponent1"); + testModel.addComponent("test0", "testComponent2"); + testModel.addComponent("test1", "testComponent3"); + testModel.addHeader("test2"); + + List<String> result = testModel.getHeaders(); + assertEquals(3, result.size()); + + assertTrue(result.contains("test0")); + assertTrue(result.contains("test1")); + assertTrue(result.contains("test2")); + + testModel.addHeader("test3"); + testModel.addHeader("test4"); + testModel.addHeader("test5"); + + result = testModel.getHeaders(); + assertEquals(6, result.size()); + + assertTrue(result.contains("test0")); + assertTrue(result.contains("test1")); + assertTrue(result.contains("test2")); + assertTrue(result.contains("test3")); + assertTrue(result.contains("test4")); + assertTrue(result.contains("test5")); + + testModel.removeHeader("test0"); + + result = testModel.getHeaders(); + assertEquals(5, result.size()); + assertFalse(result.contains("test0")); + } + + @Test + public void testAccessComponents() { + AccordionModel<String, String> testModel = new AccordionModel<>(); + testModel.addComponent("test0", "testComponent0"); + testModel.addComponent("test0", "testComponent1"); + testModel.addComponent("test0", "testComponent2"); + testModel.addComponent("test1", "testComponent3"); + testModel.addComponent("test1", "testComponent4"); + + List<String> result = testModel.getComponents("test0"); + assertEquals(3, result.size()); + + assertTrue(result.contains("testComponent0")); + assertTrue(result.contains("testComponent1")); + assertTrue(result.contains("testComponent2")); + + result = testModel.getComponents("test1"); + assertEquals(2, result.size()); + + assertTrue(result.contains("testComponent3")); + assertTrue(result.contains("testComponent4")); + + result = testModel.getComponents("test2"); + assertNotNull(result); + assertTrue(result.isEmpty()); + + testModel.addComponent("test1", "testComponent5"); + result = testModel.getComponents("test1"); + assertEquals(3, result.size()); + + testModel.removeComponent("test0", "testComponent1"); + + result = testModel.getComponents("test0"); + assertEquals(2, result.size()); + assertFalse(result.contains("testComponent1")); + } + + @Test public void testAddRemove() { final int [] results = new int[4];
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/vmlist/controller/HostTreeControllerTest.java Mon Oct 28 16:27:18 2013 +0100 @@ -0,0 +1,256 @@ +/* + * Copyright 2012, 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 + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.internal.vmlist.controller; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; + +import java.util.List; + +import javax.swing.RepaintManager; +import javax.swing.SwingUtilities; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.redhat.thermostat.client.core.vmlist.HostFilter; +import com.redhat.thermostat.client.core.vmlist.VMFilter; +import com.redhat.thermostat.client.swing.internal.accordion.Accordion; +import com.redhat.thermostat.client.swing.internal.accordion.AccordionModel; +import com.redhat.thermostat.client.swing.internal.vmlist.HostTreeComponentFactory; +import com.redhat.thermostat.storage.core.HostRef; +import com.redhat.thermostat.storage.core.VmRef; + +public class HostTreeControllerTest { + + private Accordion<HostRef, VmRef> accordion; + private DecoratorManager decoratorManager; + private HostTreeComponentFactory componentFactory; + private AccordionModel<HostRef, VmRef> proxyModel; + private DecoratorProviderExtensionListener hostDecoratorListener; + private DecoratorProviderExtensionListener vmDecoratorListener; + + @BeforeClass + public static void setUpOnce() { + // This is needed because some other test may have installed the + // EDT violation checker repaint manager. + RepaintManager.setCurrentManager(new RepaintManager()); + } + + @Before + @SuppressWarnings("unchecked") + public void setup() { + accordion = mock(Accordion.class); + decoratorManager = mock(DecoratorManager.class); + componentFactory = mock(HostTreeComponentFactory.class); + + proxyModel = new AccordionModel<>(); + when(accordion.getModel()).thenReturn(proxyModel); + + hostDecoratorListener = mock(DecoratorProviderExtensionListener.class); + vmDecoratorListener = mock(DecoratorProviderExtensionListener.class); + when(decoratorManager.getVmDecoratorListener()).thenReturn(vmDecoratorListener); + when(decoratorManager.getHostDecoratorListener()).thenReturn(hostDecoratorListener); + } + + private void waitForSwing() { + try { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + // just wait :) + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private abstract class TestHostFilter extends HostFilter { + boolean filter; + @Override + public final boolean matches(HostRef toMatch) { + if (!filter) return true; + + return matchesImpl(toMatch); + } + + protected abstract boolean matchesImpl(HostRef toMatch); + + public void toggle() { + filter = !this.filter; + notify(FilterEvent.FILTER_CHANGED); + } + } + + private abstract class TestVMFilter extends VMFilter { + boolean filter; + @Override + public final boolean matches(VmRef toMatch) { + if (!filter) return true; + + return matchesImpl(toMatch); + } + + protected abstract boolean matchesImpl(VmRef toMatch); + + public void toggle() { + filter = !this.filter; + notify(FilterEvent.FILTER_CHANGED); + } + } + + @Test + public void testController() { + HostTreeController controller = + new HostTreeController(accordion, decoratorManager, + componentFactory); + + // Add without filters + + HostRef host0 = new HostRef("0", "0"); + HostRef host1 = new HostRef("1", "1"); + + controller.registerHost(host0); + controller.registerHost(host1); + + waitForSwing(); + + // check if our model contains everything it's supposed to + List<HostRef> headers = proxyModel.getHeaders(); + assertEquals(2, headers.size()); + assertTrue(headers.contains(host0)); + assertTrue(headers.contains(host1)); + + // now with filter + + TestHostFilter filter1 = new TestHostFilter() { + @Override + protected boolean matchesImpl(HostRef toMatch) { + return (toMatch.getName().equals("0")); + } + }; + // enable the filter first + filter1.toggle(); + + controller.addHostFilter(filter1); + + waitForSwing(); + + // we need to check that the model is rebuild with the appropriate + // hosts, the filter should only let one host in + headers = proxyModel.getHeaders(); + assertEquals(1, headers.size()); + assertTrue(headers.contains(host0)); + assertFalse(headers.contains(host1)); + + // this should cause the tree to rebuild with all hosts in place again + filter1.toggle(); + + waitForSwing(); + + headers = proxyModel.getHeaders(); + assertEquals(2, headers.size()); + assertTrue(headers.contains(host0)); + assertTrue(headers.contains(host1)); + + // now on with vms, filter not enabled at first + TestVMFilter filter2 = new TestVMFilter() { + @Override + protected boolean matchesImpl(VmRef toMatch) { + return (toMatch.getName().equals("vm0")); + } + }; + + controller.addVMFilter(filter2); + + waitForSwing(); + + headers = proxyModel.getHeaders(); + assertEquals(2, headers.size()); + + VmRef vm0 = new VmRef(host0, "0", 0, "vm0"); + VmRef vm1 = new VmRef(host0, "1", 1, "vm1"); + VmRef vm2 = new VmRef(host1, "2", 2, "vm2"); + + controller.registerVM(vm0); + controller.registerVM(vm1); + controller.registerVM(vm2); + + waitForSwing(); + + List<VmRef> components = proxyModel.getComponents(host0); + assertEquals(2, components.size()); + assertTrue(components.contains(vm0)); + assertTrue(components.contains(vm1)); + + components = proxyModel.getComponents(host1); + assertEquals(1, components.size()); + assertTrue(components.contains(vm2)); + + filter2.toggle(); + + waitForSwing(); + + components = proxyModel.getComponents(host0); + assertEquals(1, components.size()); + assertTrue(components.contains(vm0)); + assertFalse(components.contains(vm1)); + + components = proxyModel.getComponents(host1); + assertTrue(components.isEmpty()); + + // now test if controller reacts to updates + + controller.updateVMStatus(vm0); + + waitForSwing(); + + verify(vmDecoratorListener).decorationChanged(); + + controller.updateHostStatus(host0); + + waitForSwing(); + + verify(hostDecoratorListener).decorationChanged(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/core/src/main/java/com/redhat/thermostat/common/AllPassFilter.java Mon Oct 28 16:27:18 2013 +0100 @@ -0,0 +1,46 @@ +/* + * Copyright 2012, 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 + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.common; + +/** + * A {@link Filter} that passes everything. + */ +public class AllPassFilter<T> extends Filter<T> { + public boolean matches(T toMatch) { + return true; + }; +}
--- a/common/core/src/main/java/com/redhat/thermostat/common/Filter.java Tue Oct 22 17:48:37 2013 +0200 +++ b/common/core/src/main/java/com/redhat/thermostat/common/Filter.java Mon Oct 28 16:27:18 2013 +0100 @@ -36,13 +36,67 @@ package com.redhat.thermostat.common; +import com.redhat.thermostat.annotations.ExtensionPoint; + /** * A {@link Filter} decides if some information matches what this - * {@link Filter} is designed to work with. + * filter is designed to work with. The exact meaning of the + * word "match" depends on the context this filter is applied. + * + * <br /><br /> + * + * For example, a {@link String} filter that match for all the input containing + * the word "test" would return {@code true} for both "a test" or "testing", + * but {@code false} for the mispelled word "tesst". This example filter could + * then be used to implement a search function, by testing various strings + * and only showing the ones that match this filter. + * + * <br /><br /> + * + * Filters may change their behavior due to external events in a certain + * context. In such cases, the filter managers specific to those context should + * register as {@link FilterEvent} listeners and the {@link Filter} + * implementation should notify of {@link FilterEvent#FILTER_CHANGED} events. + * + * <br /><br /> + * + * As an example let's take again our {@link String} filter. If such filter + * was used in a search context, the filter could react to user input and + * change the string to be used as matcher. At each matcher change, it should + * notify its listeners that such change occurred in order to allow correct + * re-processing of the filter. */ -public interface Filter<T> { +@ExtensionPoint +public abstract class Filter<T> { - boolean matches(T toMatch); + public enum FilterEvent { + FILTER_CHANGED, + } + + private final ActionNotifier<FilterEvent> notifier; + public Filter() { + notifier = new ActionNotifier<>(this); + } + + /** + * Return {@code true} if this filter match the given input, {@code false} + * otherwise. + */ + public abstract boolean matches(T toMatch); + public void addFilterEventListener(ActionListener<FilterEvent> listener) { + notifier.addActionListener(listener); + } + + public void removeFilterEventListener(ActionListener<FilterEvent> listener) { + notifier.removeActionListener(listener); + } + + /** + * Notify all the listeners that a change occurred in this filter. + */ + protected void notify(FilterEvent action) { + notifier.fireAction(action); + } }
--- a/killvm/client-swing/src/main/java/com/redhat/thermostat/killvm/client/internal/KillVMAction.java Tue Oct 22 17:48:37 2013 +0200 +++ b/killvm/client-swing/src/main/java/com/redhat/thermostat/killvm/client/internal/KillVMAction.java Mon Oct 28 16:27:18 2013 +0100 @@ -111,7 +111,7 @@ return new LocalAndAliveFilter(); } - private class LocalAndAliveFilter implements Filter<VmRef> { + private class LocalAndAliveFilter extends Filter<VmRef> { @Override public boolean matches(VmRef ref) {
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/monitor/HostMonitor.java Tue Oct 22 17:48:37 2013 +0200 +++ b/storage/core/src/main/java/com/redhat/thermostat/storage/monitor/HostMonitor.java Mon Oct 28 16:27:18 2013 +0100 @@ -36,7 +36,10 @@ package com.redhat.thermostat.storage.monitor; +import java.util.List; + import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.Filter; import com.redhat.thermostat.storage.core.HostRef; import com.redhat.thermostat.storage.core.VmRef; @@ -59,4 +62,10 @@ * Removes the listener to the given {@link HostRef} */ void removeHostChangeListener(HostRef host, ActionListener<Action> listener); + + /** + * Returns all the {@link VmRef} tracked by the given host and matching the + * given {@link Filter}. + */ + List<VmRef> getVirtualMachines(HostRef host, Filter<VmRef> matcher); }
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/monitor/NetworkMonitor.java Tue Oct 22 17:48:37 2013 +0200 +++ b/storage/core/src/main/java/com/redhat/thermostat/storage/monitor/NetworkMonitor.java Mon Oct 28 16:27:18 2013 +0100 @@ -36,7 +36,10 @@ package com.redhat.thermostat.storage.monitor; +import java.util.List; + import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.Filter; import com.redhat.thermostat.storage.core.HostRef; /** @@ -48,7 +51,12 @@ HOST_ADDED, HOST_REMOVED, } - + + /** + * Returns all the {@link HostRef} tracked matching the given {@link Filter}. + */ + List<HostRef> getHosts(Filter<HostRef> matcher); + void addNetworkChangeListener(ActionListener<Action> listener); void removeNetworkChangeListener(ActionListener<Action> listener); }
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/monitor/internal/HostMonitorImpl.java Tue Oct 22 17:48:37 2013 +0200 +++ b/storage/core/src/main/java/com/redhat/thermostat/storage/monitor/internal/HostMonitorImpl.java Mon Oct 28 16:27:18 2013 +0100 @@ -36,16 +36,21 @@ package com.redhat.thermostat.storage.monitor.internal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.ActionNotifier; +import com.redhat.thermostat.common.Filter; import com.redhat.thermostat.common.Pair; import com.redhat.thermostat.common.Timer; import com.redhat.thermostat.common.TimerFactory; import com.redhat.thermostat.storage.core.HostRef; +import com.redhat.thermostat.storage.core.VmRef; import com.redhat.thermostat.storage.dao.VmInfoDAO; import com.redhat.thermostat.storage.monitor.HostMonitor; @@ -69,6 +74,18 @@ } @Override + public List<VmRef> getVirtualMachines(HostRef host, Filter<VmRef> matcher) { + List<VmRef> vms = new ArrayList<>(); + Collection<VmRef> _vms = vmDao.getVMs(host); + for (VmRef vm : _vms) { + if (matcher.matches(vm)) { + vms.add(vm); + } + } + return vms; + } + + @Override public void addHostChangeListener(HostRef host, ActionListener<Action> listener) {
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/monitor/internal/NetworkMonitorImpl.java Tue Oct 22 17:48:37 2013 +0200 +++ b/storage/core/src/main/java/com/redhat/thermostat/storage/monitor/internal/NetworkMonitorImpl.java Mon Oct 28 16:27:18 2013 +0100 @@ -36,12 +36,17 @@ package com.redhat.thermostat.storage.monitor.internal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import java.util.concurrent.TimeUnit; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.ActionNotifier; +import com.redhat.thermostat.common.Filter; import com.redhat.thermostat.common.Timer; import com.redhat.thermostat.common.TimerFactory; +import com.redhat.thermostat.storage.core.HostRef; import com.redhat.thermostat.storage.dao.HostInfoDAO; import com.redhat.thermostat.storage.monitor.NetworkMonitor; @@ -52,9 +57,12 @@ protected final ActionNotifier<NetworkMonitor.Action> notifier; private Timer timer; + private HostInfoDAO hostDAO; public NetworkMonitorImpl(TimerFactory timerFactory, HostInfoDAO hostDAO) { + this.hostDAO = hostDAO; + notifier = new ActionNotifier<>(this); timer = timerFactory.createTimer(); @@ -65,6 +73,18 @@ } @Override + public List<HostRef> getHosts(Filter<HostRef> matcher) { + List<HostRef> hosts = new ArrayList<>(); + Collection<HostRef> _hosts = hostDAO.getHosts(); + for (HostRef host : _hosts) { + if (matcher.matches(host)) { + hosts.add(host); + } + } + return hosts; + } + + @Override public void addNetworkChangeListener(ActionListener<Action> listener) { notifier.addActionListener(listener); if (notifier.listenersCount() == 1) {
--- a/storage/core/src/test/java/com/redhat/thermostat/storage/monitor/internal/HostMonitorImplTest.java Tue Oct 22 17:48:37 2013 +0200 +++ b/storage/core/src/test/java/com/redhat/thermostat/storage/monitor/internal/HostMonitorImplTest.java Mon Oct 28 16:27:18 2013 +0100 @@ -45,6 +45,8 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertEquals; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -53,10 +55,13 @@ import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.ActionNotifier; +import com.redhat.thermostat.common.AllPassFilter; +import com.redhat.thermostat.common.Filter; import com.redhat.thermostat.common.Pair; import com.redhat.thermostat.common.Timer; import com.redhat.thermostat.common.TimerFactory; import com.redhat.thermostat.storage.core.HostRef; +import com.redhat.thermostat.storage.core.VmRef; import com.redhat.thermostat.storage.dao.VmInfoDAO; import com.redhat.thermostat.storage.monitor.HostMonitor; @@ -64,11 +69,66 @@ private VmInfoDAO vmDao; private TimerFactory timerFactory; + private Timer timer1; @Before public void setup() { vmDao = mock(VmInfoDAO.class); timerFactory = mock(TimerFactory.class); + timer1 = mock(Timer.class); + when(timerFactory.createTimer()).thenReturn(timer1); + } + + @Test + public void testGetVirtualMachines() { + List<VmRef> testData = new ArrayList<>(); + List<VmRef> testData2 = new ArrayList<>(); + + HostRef host0 = new HostRef("0", "0"); + HostRef host1 = new HostRef("1", "1"); + + VmRef vm0 = new VmRef(host0, "0", 0, "0"); + VmRef vm1 = new VmRef(host0, "1", 1, "2"); + VmRef vm2 = new VmRef(host0, "2", 2, "3"); + VmRef vm3 = new VmRef(host0, "3", 3, "3"); + VmRef vm4 = new VmRef(host0, "4", 4, "4"); + VmRef vm5 = new VmRef(host0, "5", 5, "5"); + + testData.add(vm0); + testData.add(vm1); + testData.add(vm2); + testData.add(vm3); + testData.add(vm4); + testData.add(vm5); + + when(vmDao.getVMs(host0)).thenReturn(testData); + when(vmDao.getVMs(host1)).thenReturn(testData2); + + HostMonitor monitor = new HostMonitorImpl(timerFactory, vmDao); + List<VmRef> vms = monitor.getVirtualMachines(host0, new AllPassFilter<VmRef>()); + assertEquals(testData.size(), vms.size()); + for (VmRef ref : testData) { + assertTrue(vms.contains(ref)); + } + + vms = monitor.getVirtualMachines(host1, new AllPassFilter<VmRef>()); + assertEquals(0, vms.size()); + + Filter<VmRef> bandFilter = new Filter<VmRef>() { + @Override + public boolean matches(VmRef toMatch) { + return toMatch.getName().equals("1") || + toMatch.getName().equals("2") || + toMatch.getName().equals("3"); + } + }; + + vms = monitor.getVirtualMachines(host0, bandFilter); + assertEquals(3, vms.size()); + + assertTrue(vms.contains(vm1)); + assertTrue(vms.contains(vm2)); + assertTrue(vms.contains(vm3)); } @SuppressWarnings("unchecked") @@ -77,9 +137,6 @@ ActionListener<HostMonitor.Action> listener1 = mock(ActionListener.class); ActionListener<HostMonitor.Action> listener2 = mock(ActionListener.class); - Timer timer1 = mock(Timer.class); - when(timerFactory.createTimer()).thenReturn(timer1); - HostRef host1 = new HostRef("0", "0"); HostMonitor monitor = new HostMonitorImpl(timerFactory, vmDao);
--- a/storage/core/src/test/java/com/redhat/thermostat/storage/monitor/internal/NetworkMonitorImplTest.java Tue Oct 22 17:48:37 2013 +0200 +++ b/storage/core/src/test/java/com/redhat/thermostat/storage/monitor/internal/NetworkMonitorImplTest.java Mon Oct 28 16:27:18 2013 +0100 @@ -40,15 +40,22 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.Mockito.times; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.TimeUnit; import org.junit.Before; import org.junit.Test; import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.AllPassFilter; +import com.redhat.thermostat.common.Filter; import com.redhat.thermostat.common.Timer; import com.redhat.thermostat.common.TimerFactory; +import com.redhat.thermostat.storage.core.HostRef; import com.redhat.thermostat.storage.dao.HostInfoDAO; import com.redhat.thermostat.storage.monitor.NetworkMonitor; import com.redhat.thermostat.storage.monitor.NetworkMonitor.Action; @@ -57,11 +64,58 @@ private HostInfoDAO hostDao; private TimerFactory timerFactory; - + private Timer timer; + @Before public void setup() { hostDao = mock(HostInfoDAO.class); timerFactory = mock(TimerFactory.class); + timer = mock(Timer.class); + when(timerFactory.createTimer()).thenReturn(timer); + } + + @Test + public void testGetHost() { + List<HostRef> testData = new ArrayList<>(); + HostRef ref0 = new HostRef("0", "test#0"); + HostRef ref1 = new HostRef("1", "test#1"); + HostRef ref2 = new HostRef("2", "test#2"); + HostRef ref3 = new HostRef("3", "test#3"); + HostRef ref4 = new HostRef("4", "test#4"); + HostRef ref5 = new HostRef("5", "test#5"); + + testData.add(ref0); + testData.add(ref1); + testData.add(ref2); + testData.add(ref3); + testData.add(ref4); + testData.add(ref5); + + when(hostDao.getHosts()).thenReturn(testData); + + NetworkMonitor monitor = new NetworkMonitorImpl(timerFactory, hostDao); + List<HostRef> hosts = monitor.getHosts(new AllPassFilter<HostRef>()); + assertEquals(testData.size(), hosts.size()); + + for (HostRef ref : testData) { + assertTrue(hosts.contains(ref)); + } + + Filter<HostRef> bandFilter = new Filter<HostRef>() { + @Override + public boolean matches(HostRef toMatch) { + return toMatch.getName().equals("test#1") || + toMatch.getName().equals("test#2") || + toMatch.getName().equals("test#3"); + } + }; + + hosts = monitor.getHosts(bandFilter); + assertEquals(3, hosts.size()); + + assertTrue(hosts.contains(ref1)); + assertTrue(hosts.contains(ref2)); + assertTrue(hosts.contains(ref3)); } @SuppressWarnings("unchecked") @@ -70,9 +124,6 @@ ActionListener<Action> listener1 = mock(ActionListener.class); ActionListener<Action> listener2 = mock(ActionListener.class); - - Timer timer = mock(Timer.class); - when(timerFactory.createTimer()).thenReturn(timer); NetworkMonitor monitor = new NetworkMonitorImpl(timerFactory, hostDao); monitor.addNetworkChangeListener(listener1);