changeset 1325:2228cf23604c

Restore search for References in the Reference View review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-November/008749.html reviewed-by: omajid
author Mario Torre <neugens.limasoftware@gmail.com>
date Thu, 14 Nov 2013 18:17:51 +0100
parents 8e37e473ef52
children 921506d81225
files client/core/src/main/java/com/redhat/thermostat/client/ui/SearchProvider.java client/living-vm-filter/swing/src/main/java/com/redhat/thermostat/client/filter/host/swing/HostVmMainLabelDecorator.java client/swing/pom.xml client/swing/src/main/java/com/redhat/thermostat/client/swing/components/SearchField.java client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainView.java client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindow.java client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindowControllerImpl.java client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/VMMonitorController.java client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/search/BaseSearchProvider.java client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/search/ReferenceFieldSearchFilter.java client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/search/SearchBackend.java client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/search/SearchField.java client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/sidepane/ThermostatSidePanel.java client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/sidepane/TopSidePane.java client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/vmlist/controller/HostTreeController.java client/swing/src/test/java/com/redhat/thermostat/client/swing/components/SearchFieldTest.java client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/VMMonitorControllerTest.java client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/search/ReferenceFieldSearchFilterTest.java client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/search/SearchBackendTest.java client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/vmlist/controller/HostTreeControllerTest.java vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/ObjectDetailsPanel.java
diffstat 21 files changed, 944 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/SearchProvider.java	Thu Nov 14 18:17:51 2013 +0100
@@ -0,0 +1,61 @@
+/*
+ * 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.ui;
+
+import com.redhat.thermostat.common.ActionListener;
+
+/**
+ * Implementations of this class can be used to notified listeners that a
+ * search is performed. The search term is used as a payload for the event.
+ * 
+ * <br /><br />
+ * 
+ * The exact type of the search term and conditions upon which the event is
+ * fired are implementation dependent.
+ * 
+ * <br /><br />
+ * 
+ * This interface is <strong>not</strong> a service.
+ */
+public interface SearchProvider {
+    public enum SearchAction {
+        PERFORM_SEARCH,
+    }
+    
+    public void addSearchListener(ActionListener<SearchAction> listener);
+    public void removeSearchListener(ActionListener<SearchAction> listener);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/living-vm-filter/swing/src/main/java/com/redhat/thermostat/client/filter/host/swing/HostVmMainLabelDecorator.java	Thu Nov 14 18:17:51 2013 +0100
@@ -0,0 +1,53 @@
+/*
+ * 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.filter.host.swing;
+
+import com.redhat.thermostat.client.ui.ReferenceFieldLabelDecorator;
+import com.redhat.thermostat.storage.core.Ref;
+
+public class HostVmMainLabelDecorator implements ReferenceFieldLabelDecorator {
+
+    @Override
+    public String getLabel(String originalLabel, Ref reference) {
+        return reference.getName();
+    }
+    
+    @Override
+    public int getOrderValue() {
+        return ORDER_FIRST;
+    }
+}
--- a/client/swing/pom.xml	Thu Nov 14 18:17:50 2013 +0100
+++ b/client/swing/pom.xml	Thu Nov 14 18:17:51 2013 +0100
@@ -183,6 +183,7 @@
               com.redhat.thermostat.client.swing.internal.vmlist.controller,
               com.redhat.thermostat.client.swing.internal.accordion,
               com.redhat.thermostat.client.swing.internal.registry.decorator,
+              com.redhat.thermostat.client.swing.internal.search,
             </Private-Package>
             <!-- Do not autogenerate uses clauses in Manifests -->
             <_nouses>true</_nouses>
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/SearchField.java	Thu Nov 14 18:17:50 2013 +0100
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/SearchField.java	Thu Nov 14 18:17:51 2013 +0100
@@ -47,7 +47,6 @@
 
 import javax.swing.BorderFactory;
 import javax.swing.JLabel;
-import javax.swing.JPanel;
 import javax.swing.JTextField;
 import javax.swing.event.DocumentEvent;
 import javax.swing.event.DocumentListener;
@@ -56,8 +55,7 @@
 
 import com.redhat.thermostat.client.locale.LocaleResources;
 import com.redhat.thermostat.client.swing.IconResource;
-import com.redhat.thermostat.common.ActionListener;
-import com.redhat.thermostat.common.ActionNotifier;
+import com.redhat.thermostat.client.swing.internal.search.BaseSearchProvider;
 import com.redhat.thermostat.shared.locale.LocalizedString;
 import com.redhat.thermostat.shared.locale.Translate;
 
@@ -67,19 +65,14 @@
  * Similar to other swing components, this component should only be
  * modified on the swing EDT.
  */
-public class SearchField extends JPanel {
+@SuppressWarnings("serial")
+public class SearchField extends BaseSearchProvider {
 
     /** For use by tests only */
     public static final String VIEW_NAME = "searchField";
 
-    public enum SearchAction {
-        TEXT_CHANGED,
-        PERFORM_SEARCH,
-    }
-
     private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
 
-    private final ActionNotifier<SearchAction> notifier = new ActionNotifier<>(this);
     private final JTextField searchField = new JTextField();
 
     private final AtomicReference<String> searchText = new AtomicReference<String>("");
@@ -87,7 +80,7 @@
     private final AtomicBoolean labelDisplayed = new AtomicBoolean(true);
 
     public SearchField() {
-        super(new BorderLayout());
+        setLayout(new BorderLayout());
 
         // TODO move this icon inside the search field
         JLabel searchIcon = new JLabel(IconResource.SEARCH.getIcon());
@@ -129,7 +122,7 @@
                     searchText.set(filter);
                     if (!(filter.equals(previousText))) {
                         previousText = filter;
-                        fireViewAction(SearchAction.TEXT_CHANGED);
+                        fireViewAction(SearchAction.PERFORM_SEARCH, searchText.get());
                     }
                 }
             }
@@ -161,7 +154,7 @@
         final java.awt.event.ActionListener searchActionListener = new java.awt.event.ActionListener() {
             @Override
             public void actionPerformed(ActionEvent e) {
-                fireViewAction(SearchAction.PERFORM_SEARCH);
+                fireViewAction(SearchAction.PERFORM_SEARCH, searchField.getText());
             }
         };
 
@@ -191,17 +184,5 @@
     public void setTooltip(final LocalizedString tooltip) {
         searchField.setToolTipText(tooltip.getContents());
     }
-
-    public void addActionListener(ActionListener<SearchAction> listener) {
-        notifier.addActionListener(listener);
-    }
-
-    public void removeActionListener(ActionListener<SearchAction> listener) {
-        notifier.removeActionListener(listener);
-    }
-
-    private void fireViewAction(SearchAction action) {
-        notifier.fireAction(action);
-    }
 }
 
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainView.java	Thu Nov 14 18:17:50 2013 +0100
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainView.java	Thu Nov 14 18:17:51 2013 +0100
@@ -40,6 +40,7 @@
 
 import com.redhat.thermostat.client.core.progress.ProgressNotifier;
 import com.redhat.thermostat.client.core.views.BasicView;
+import com.redhat.thermostat.client.swing.internal.search.ReferenceFieldSearchFilter;
 import com.redhat.thermostat.client.swing.internal.vmlist.controller.ContextActionController;
 import com.redhat.thermostat.client.swing.internal.vmlist.controller.HostTreeController;
 import com.redhat.thermostat.client.ui.MenuAction;
@@ -100,5 +101,11 @@
      * actions in the UI Client.
      */
     ContextActionController getContextActionController();
+
+    /**
+     * Returns the filter used for searching references inside the reference
+     * tree.
+     */
+    ReferenceFieldSearchFilter getSearchFilter();
 }
 
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindow.java	Thu Nov 14 18:17:50 2013 +0100
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindow.java	Thu Nov 14 18:17:51 2013 +0100
@@ -80,6 +80,8 @@
 import com.redhat.thermostat.client.swing.internal.progress.ProgressNotificationArea;
 import com.redhat.thermostat.client.swing.internal.progress.SwingProgressNotifier;
 import com.redhat.thermostat.client.swing.internal.progress.SwingProgressNotifier.PropertyChange;
+import com.redhat.thermostat.client.swing.internal.search.ReferenceFieldSearchFilter;
+import com.redhat.thermostat.client.swing.internal.search.SearchField;
 import com.redhat.thermostat.client.swing.internal.sidepane.ExpanderComponent;
 import com.redhat.thermostat.client.swing.internal.sidepane.ThermostatSidePanel;
 import com.redhat.thermostat.client.swing.internal.splitpane.ThermostatSplitPane;
@@ -120,6 +122,8 @@
     
     private HostTreeController hostTreeController;
     private ContextActionController contextActionController;
+
+    private ReferenceFieldSearchFilter filter;
     
     public MainWindow() {
         super();
@@ -139,7 +143,7 @@
         setupPanels(glassPane);
 
         this.setPreferredSize(new Dimension(800, 600));
-
+        
         statusBar = new StatusBar();
         setupNotificationPane(statusBar, glassPane);
         
@@ -338,6 +342,17 @@
                 }
             }
         });
+
+        installSearchFiled();
+    }
+    
+    private void installSearchFiled() {
+        // install the search field in the sidepane for now
+        SearchField searchField = new SearchField();
+        navigationPanel.getTopPane().add(searchField);
+        
+        filter = new ReferenceFieldSearchFilter(searchField, hostTreeController);
+        hostTreeController.addFilter(filter);
     }
     
     private JPanel createDetailsPanel() {
@@ -472,5 +487,10 @@
     public ContextActionController getContextActionController() {
         return contextActionController;
     }
+    
+    @Override
+    public ReferenceFieldSearchFilter getSearchFilter() {
+        return filter;
+    }
 }
 
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindowControllerImpl.java	Thu Nov 14 18:17:50 2013 +0100
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindowControllerImpl.java	Thu Nov 14 18:17:51 2013 +0100
@@ -59,6 +59,7 @@
 import com.redhat.thermostat.client.swing.internal.osgi.ContextActionServiceTracker;
 import com.redhat.thermostat.client.swing.internal.osgi.InformationServiceTracker;
 import com.redhat.thermostat.client.swing.internal.registry.decorator.DecoratorRegistryController;
+import com.redhat.thermostat.client.swing.internal.search.ReferenceFieldSearchFilter;
 import com.redhat.thermostat.client.swing.internal.vmlist.controller.ContextActionController;
 import com.redhat.thermostat.client.swing.internal.vmlist.controller.ContextHandler;
 import com.redhat.thermostat.client.swing.internal.vmlist.controller.FilterManager;
@@ -288,17 +289,20 @@
     
     private void initHostVMTree() {
         HostTreeController hostController = view.getHostTreeController();
+        ReferenceFieldSearchFilter filter = view.getSearchFilter();
         
         // 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);
-
+            filter.addHost(host);
+            
             // get the vm for this host
             List<VmRef> vms = hostMonitor.getVirtualMachines(host, vmFilter);
             for (VmRef vm : vms) {
                 hostController.registerVM(vm);
+                filter.addVM(vm);
             }
         }
     }
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/VMMonitorController.java	Thu Nov 14 18:17:50 2013 +0100
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/VMMonitorController.java	Thu Nov 14 18:17:51 2013 +0100
@@ -36,6 +36,7 @@
 
 package com.redhat.thermostat.client.swing.internal;
 
+import com.redhat.thermostat.client.swing.internal.search.ReferenceFieldSearchFilter;
 import com.redhat.thermostat.common.ActionEvent;
 import com.redhat.thermostat.common.ActionListener;
 import com.redhat.thermostat.storage.core.HostRef;
@@ -73,11 +74,13 @@
             switch (actionEvent.getActionId()) {
             case HOST_ADDED:
                 view.getHostTreeController().registerHost(host);
+                view.getSearchFilter().addHost(host);
                 hostMonitor.addHostChangeListener(host, hostListener);
                 break;
 
             case HOST_REMOVED:
                 view.getHostTreeController().updateHostStatus(host);
+                view.getSearchFilter().removeHost(host);
                 hostMonitor.removeHostChangeListener(host, hostListener);
                 break;
                 
@@ -96,11 +99,12 @@
             switch (actionEvent.getActionId()) {
             case VM_ADDED:
                 view.getHostTreeController().registerVM(vm);
+                view.getSearchFilter().addVM(vm);
                 break;
             
             case VM_REMOVED:
                 view.getHostTreeController().updateVMStatus(vm);
-                
+                view.getSearchFilter().removeVM(vm);
             default:
                 break;
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/search/BaseSearchProvider.java	Thu Nov 14 18:17:51 2013 +0100
@@ -0,0 +1,60 @@
+/*
+ * 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.search;
+
+import javax.swing.JPanel;
+
+import com.redhat.thermostat.client.ui.SearchProvider;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.ActionNotifier;
+
+@SuppressWarnings("serial")
+public class BaseSearchProvider extends JPanel implements SearchProvider {
+    private final ActionNotifier<SearchAction> notifier = new ActionNotifier<>(this);
+    
+    public void addSearchListener(ActionListener<SearchAction> listener) {
+        notifier.addActionListener(listener);
+    }
+
+    public void removeSearchListener(ActionListener<SearchAction> listener) {
+        notifier.removeActionListener(listener);
+    }
+
+    protected void fireViewAction(SearchAction action, String text) {
+        notifier.fireAction(action, text);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/search/ReferenceFieldSearchFilter.java	Thu Nov 14 18:17:51 2013 +0100
@@ -0,0 +1,140 @@
+/*
+ * 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.search;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import com.redhat.thermostat.client.swing.internal.vmlist.controller.HostTreeController;
+import com.redhat.thermostat.client.ui.ReferenceFilter;
+import com.redhat.thermostat.client.ui.SearchProvider;
+import com.redhat.thermostat.client.ui.SearchProvider.SearchAction;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.core.Ref;
+import com.redhat.thermostat.storage.core.VmRef;
+
+/**
+ * NOTE: This filter is kept private because the Search API is incomplete at
+ * this point, will be a separate plugin when public search mechanism is in
+ * place.
+ */
+public class ReferenceFieldSearchFilter extends ReferenceFilter implements ActionListener<SearchAction> {
+        private SearchProvider searchProvider;
+    private AtomicReference<String> searchString;
+    
+    private HostTreeController hostTreeController;
+
+    private SearchBackend backend;
+    
+    public ReferenceFieldSearchFilter(SearchProvider provider,
+                                      HostTreeController hostTreeController)
+    {
+        this.searchProvider = provider;
+        this.searchProvider.addSearchListener(this);
+        this.searchString = new AtomicReference<>("");
+        
+        this.hostTreeController = hostTreeController;
+        backend = new SearchBackend();
+    }
+   
+    /**
+     * For testing only
+     */
+    void setBackend(SearchBackend backend) {
+        this.backend = backend;
+    }
+    
+    @Override
+    public boolean applies(Ref reference) {
+        boolean applies = false;
+        
+        String search = searchString.get();
+        if (!search.isEmpty()) {
+            applies = true;
+        }
+        
+        return applies;
+    }
+    
+    @Override
+    public boolean matches(Ref reference) {
+        
+        String search = searchString.get();
+        if (search.isEmpty()) {
+            return true;
+        }
+
+        boolean match = backend.match(search, reference);
+        if (match && (reference instanceof HostRef)) {
+            // ask to expand this node, in case it's not
+            hostTreeController.expandNode((HostRef) reference);
+        }
+        return match;
+    }
+    
+    @Override
+    public void actionPerformed(ActionEvent<SearchAction> actionEvent) {
+        
+        String oldFilter = searchString.get();
+        
+        String filter = (String) actionEvent.getPayload();
+        if (filter != null) {
+            
+            if (!oldFilter.equals(filter)) {
+                searchString.set(filter);
+            } else {
+                searchString.set("");
+            }
+            notify(FilterEvent.FILTER_CHANGED);
+        }
+    }
+    
+    public void addHost(HostRef host) {
+        backend.addHost(host);
+        notify(FilterEvent.FILTER_CHANGED);
+    }
+
+    public void removeHost(HostRef host) {}
+
+    public void addVM(VmRef vm) {
+        backend.addVM(vm);
+        notify(FilterEvent.FILTER_CHANGED);
+    }
+
+    public void removeVM(VmRef vm) {}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/search/SearchBackend.java	Thu Nov 14 18:17:51 2013 +0100
@@ -0,0 +1,95 @@
+/*
+ * 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.search;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.core.Ref;
+import com.redhat.thermostat.storage.core.VmRef;
+
+/**
+ *
+ */
+class SearchBackend {
+    
+    private HashMap<HostRef, Set<VmRef>> hosts;
+    
+    public SearchBackend() {
+        hosts = new HashMap<>();
+    }
+    
+    public synchronized boolean match(String pattern, Ref reference) {
+
+        boolean match = false;
+        
+        String searchString = pattern.toLowerCase();
+        
+        String name = reference.getName().toLowerCase();
+        String id = reference.getStringID().toLowerCase();
+        
+        match = name.contains(searchString) || id.contains(searchString);
+        if (!match && (reference instanceof HostRef)) {
+            HostRef host = (HostRef) reference;
+            if (hosts.containsKey(host)) {
+                for (VmRef vm : hosts.get(host)) {
+                    match = match(searchString, vm);
+                
+                    // since this is an host, no need to do all, as long as
+                    // we know there's at least one match, this host will
+                    // show up in the reference tree
+                    if (match) {
+                        break;
+                    }
+                }
+            }
+        }
+        
+        return match;
+    }
+    
+    public synchronized void addVM(VmRef vm) {
+        HostRef ref = vm.getHostRef();
+        hosts.get(ref).add(vm);
+    }
+
+    public synchronized void addHost(HostRef host) {
+        hosts.put(host, new HashSet<VmRef>());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/search/SearchField.java	Thu Nov 14 18:17:51 2013 +0100
@@ -0,0 +1,132 @@
+/*
+ * 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.search;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Shape;
+
+import javax.swing.Box;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.border.EmptyBorder;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+
+import com.redhat.thermostat.client.swing.GraphicsUtils;
+import com.redhat.thermostat.client.swing.components.FontAwesomeIcon;
+import com.redhat.thermostat.client.swing.components.Icon;
+import com.redhat.thermostat.client.ui.Palette;
+
+@SuppressWarnings("serial")
+public class SearchField extends BaseSearchProvider {
+
+    private JTextField searchField;
+    
+    public SearchField() {
+        setFocusable(true);
+        
+        setLayout(new BorderLayout());
+        
+        final Icon searchIcon =
+                new FontAwesomeIcon('\uf002', 12, Palette.DARK_GRAY.getColor());
+                
+        searchField = new JTextField(20);
+        
+        final JLabel searchLabel = new JLabel(searchIcon);
+
+        JPanel iconPanel = new JPanel();
+        iconPanel.setLayout(new BorderLayout());
+        iconPanel.add(searchLabel, BorderLayout.CENTER);
+        iconPanel.add(Box.createRigidArea(new Dimension(2, 5)), BorderLayout.WEST);
+
+        iconPanel.setOpaque(false);
+        
+        add(iconPanel, BorderLayout.WEST);
+        add(searchField, BorderLayout.CENTER);
+        
+        searchField.setBackground(Palette.WHITE.getColor());
+        searchField.setBorder(new EmptyBorder(0, 5, 0, 0));
+        searchField.setForeground(Palette.DARK_GRAY.getColor());
+        
+        searchField.getCaret().setBlinkRate(0);
+        searchField.setCaretColor(Palette.DARK_GRAY.getColor());
+
+        setOpaque(false);
+        setBorder(new EmptyBorder(0, 0, 0, 0));
+        
+        searchField.getDocument().addDocumentListener(new DocumentListener() {
+            @Override
+            public void changedUpdate(DocumentEvent e) {
+                Document document = e.getDocument();
+                try {
+                    String text = document.getText(0, document.getLength());
+                    fireViewAction(SearchAction.PERFORM_SEARCH, text);
+                    
+                } catch (BadLocationException ignore) {}
+            }
+            
+            @Override
+            public void insertUpdate(DocumentEvent e) {
+                changedUpdate(e);
+            }
+            
+            @Override
+            public void removeUpdate(DocumentEvent e) {
+                changedUpdate(e);
+            }
+        });
+    }
+    
+    @Override
+    protected void paintComponent(Graphics g) {
+        GraphicsUtils utils = GraphicsUtils.getInstance();
+        Graphics2D graphics = utils.createAAGraphics(g);
+
+        graphics.setPaint(Palette.WHITE.getColor());
+        
+        Shape shape = utils.getRoundShape(getWidth(), getHeight());
+        graphics.fill(shape);
+        
+        graphics.dispose();
+    }
+}
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/sidepane/ThermostatSidePanel.java	Thu Nov 14 18:17:50 2013 +0100
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/sidepane/ThermostatSidePanel.java	Thu Nov 14 18:17:51 2013 +0100
@@ -60,7 +60,6 @@
     private JPanel bottom;
     
     public ThermostatSidePanel() {
-        
         setLayout(new BorderLayout());
         
         top = new TopSidePane();
@@ -93,4 +92,8 @@
         bottom.add(comp, contraints);
         repaint();
     }
+    
+    public JPanel getTopPane() {
+        return top;
+    }
 }
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/sidepane/TopSidePane.java	Thu Nov 14 18:17:50 2013 +0100
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/sidepane/TopSidePane.java	Thu Nov 14 18:17:51 2013 +0100
@@ -51,7 +51,6 @@
 import com.redhat.thermostat.client.swing.components.DebugBorder;
 import com.redhat.thermostat.client.swing.components.FontAwesomeIcon;
 import com.redhat.thermostat.client.swing.components.Icon;
-
 import com.redhat.thermostat.client.swing.internal.vmlist.UIDefaultsImpl;
 
 @SuppressWarnings("serial")
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/vmlist/controller/HostTreeController.java	Thu Nov 14 18:17:50 2013 +0100
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/vmlist/controller/HostTreeController.java	Thu Nov 14 18:17:51 2013 +0100
@@ -203,8 +203,7 @@
                     // next filtering step
                     proxyModel.removeHeader(vm.getHostRef());
                     
-                } else
-                    if (filter(filters, vm)) {
+                } else if (filter(filters, vm)) {
                     proxyModel.removeComponent(vm.getHostRef(), vm);
                 }
             }
@@ -343,7 +342,12 @@
     private class FilterListener implements ActionListener<Filter.FilterEvent> {
         @Override
         public void actionPerformed(ActionEvent<FilterEvent> actionEvent) {
-            rebuildTree();
+            SwingUtilities.invokeLater(new Runnable() {
+                @Override
+                public void run() {
+                    rebuildTree();
+                }
+            });
         }
     }
     
@@ -369,4 +373,13 @@
         filter.removeFilterEventListener(filterListener);
         rebuildTree();
     }
+
+    public void expandNode(final HostRef reference) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                accordion.setExpanded(reference, true);
+            }
+        });
+    }
 }
\ No newline at end of file
--- a/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/SearchFieldTest.java	Thu Nov 14 18:17:50 2013 +0100
+++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/SearchFieldTest.java	Thu Nov 14 18:17:51 2013 +0100
@@ -200,16 +200,16 @@
 
         JTextComponentFixture textBox = frameFixture.textBox(SearchField.VIEW_NAME);
 
-        searchField.addActionListener(listener);
+        searchField.addSearchListener(listener);
 
         textBox.enterText(SEARCH_TEXT);
 
         verify(listener, times(SEARCH_TEXT.length())).actionPerformed(
-                new ActionEvent<SearchField.SearchAction>(searchField, SearchField.SearchAction.TEXT_CHANGED));
+                new ActionEvent<SearchField.SearchAction>(searchField, SearchField.SearchAction.PERFORM_SEARCH));
 
         textBox.enterText("\n");
 
-        verify(listener).actionPerformed(
+        verify(listener, times(SEARCH_TEXT.length() + 1)).actionPerformed(
                 new ActionEvent<SearchField.SearchAction>(searchField, SearchField.SearchAction.PERFORM_SEARCH));
 
     }
--- a/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/VMMonitorControllerTest.java	Thu Nov 14 18:17:50 2013 +0100
+++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/VMMonitorControllerTest.java	Thu Nov 14 18:17:51 2013 +0100
@@ -39,15 +39,13 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.times;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
 import com.redhat.thermostat.client.swing.internal.VMMonitorController.NetworkChangeListener;
+import com.redhat.thermostat.client.swing.internal.search.ReferenceFieldSearchFilter;
 import com.redhat.thermostat.client.swing.internal.vmlist.controller.HostTreeController;
 import com.redhat.thermostat.common.ActionEvent;
 import com.redhat.thermostat.common.ActionListener;
@@ -62,7 +60,8 @@
     private HostMonitor hostMonitor;
     private MainView view;
     private HostTreeController treeController;
-    
+    private ReferenceFieldSearchFilter searchFilter;
+
     @Before
     public void setUp() {
         networkMonitor = mock(NetworkMonitor.class);
@@ -70,6 +69,10 @@
         view = mock(MainView.class);
         treeController = mock(HostTreeController.class);
         when(view.getHostTreeController()).thenReturn(treeController);
+        
+        searchFilter = mock(ReferenceFieldSearchFilter.class);
+        when(view.getSearchFilter()).thenReturn(searchFilter);
+
     }
     
     @SuppressWarnings({ "rawtypes", "unchecked" })
@@ -96,7 +99,8 @@
         networkListener.actionPerformed(event);
         
         verify(treeController).registerHost(host1);
-        
+        verify(searchFilter).addHost(host1);
+
         event = new ActionEvent<NetworkMonitor.Action>(networkMonitor,
                                                        Action.HOST_REMOVED);
         event.setPayload(host1);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/search/ReferenceFieldSearchFilterTest.java	Thu Nov 14 18:17:51 2013 +0100
@@ -0,0 +1,176 @@
+/*
+ * 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.search;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.client.swing.internal.vmlist.controller.HostTreeController;
+import com.redhat.thermostat.client.ui.SearchProvider;
+import com.redhat.thermostat.client.ui.SearchProvider.SearchAction;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.core.Ref;
+import com.redhat.thermostat.storage.core.VmRef;
+
+/**
+ *
+ */
+public class ReferenceFieldSearchFilterTest {
+
+    private SearchProvider provider;
+    private HostTreeController hostTreeController;
+    
+    @Before
+    public void setUp() {
+        provider = mock(SearchProvider.class);
+        hostTreeController = mock(HostTreeController.class);
+    }
+    
+    @Test
+    public void testFilterApplies() {
+
+        ReferenceFieldSearchFilter filter = new ReferenceFieldSearchFilter(provider, hostTreeController);
+        assertFalse(filter.applies(mock(Ref.class)));
+        
+        ActionEvent<SearchAction> actionEvent =
+                new ActionEvent<SearchProvider.SearchAction>(this, SearchAction.PERFORM_SEARCH);
+        actionEvent.setPayload("some search string");
+        
+        filter.actionPerformed(actionEvent);
+        
+        assertTrue(filter.applies(mock(Ref.class)));
+    }
+    
+    @Test
+    public void testSearchMatchFilterInactive() {
+        
+        HostRef host0 = new HostRef("h0", "host#0");
+        VmRef vm0 = new VmRef(host0, "v0", 0, "vm#0");
+        
+        ReferenceFieldSearchFilter filter = new ReferenceFieldSearchFilter(provider, hostTreeController);
+        
+        assertTrue(filter.matches(host0));
+        assertTrue(filter.matches(vm0));
+    }
+
+    @Test
+    public void testSearchHandledCorrectly() {
+        
+        HostRef host0 = new HostRef("h0", "host#0");
+        VmRef vm0 = new VmRef(host0, "v0", 0, "vm#0");
+        
+        SearchBackend backend = mock(SearchBackend.class);
+        
+        ReferenceFieldSearchFilter filter = new ReferenceFieldSearchFilter(provider, hostTreeController);
+        filter.setBackend(backend);
+        
+        ActionEvent<SearchAction> actionEvent =
+                new ActionEvent<SearchProvider.SearchAction>(this, SearchAction.PERFORM_SEARCH);
+        actionEvent.setPayload("some search string");
+        
+        filter.actionPerformed(actionEvent);
+        
+        filter.matches(host0);
+        
+        verify(backend).match("some search string", host0);
+        
+        filter.matches(vm0);
+        
+        verify(backend).match("some search string", vm0);
+    }
+    
+    @Test
+    public void testTreeExpandedForHostOnly() {
+        
+        HostRef host0 = new HostRef("h0", "host#0");
+        VmRef vm0 = new VmRef(host0, "v0", 0, "vm#0");
+        
+        SearchBackend backend = mock(SearchBackend.class);
+        when(backend.match(any(String.class), any(HostRef.class))).thenReturn(true);
+        when(backend.match(any(String.class), any(VmRef.class))).thenReturn(true);
+
+        ReferenceFieldSearchFilter filter = new ReferenceFieldSearchFilter(provider, hostTreeController);
+        filter.setBackend(backend);
+        
+        ActionEvent<SearchAction> actionEvent =
+                new ActionEvent<SearchProvider.SearchAction>(this, SearchAction.PERFORM_SEARCH);
+        actionEvent.setPayload("some search string");
+        
+        filter.actionPerformed(actionEvent);
+        
+        boolean result = filter.matches(host0);
+        assertTrue(result);
+        
+        verify(backend).match("some search string", host0);
+        verify(hostTreeController).expandNode(host0);
+        
+        result = filter.matches(vm0);
+        assertTrue(result);
+
+        verify(backend).match("some search string", vm0);
+        verifyNoMoreInteractions(hostTreeController);
+    }
+    
+    @Test
+    public void testAddReferenceProxiedToBackend() {
+        
+        HostRef host0 = new HostRef("h0", "host#0");
+        VmRef vm0 = new VmRef(host0, "v0", 0, "vm#0");
+        
+        SearchBackend backend = mock(SearchBackend.class);
+
+        ReferenceFieldSearchFilter filter = new ReferenceFieldSearchFilter(provider, hostTreeController);
+        filter.setBackend(backend);
+        
+        filter.addHost(host0);
+        verify(backend).addHost(host0);
+        
+        filter.addVM(vm0);
+        verify(backend).addVM(vm0);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/search/SearchBackendTest.java	Thu Nov 14 18:17:51 2013 +0100
@@ -0,0 +1,143 @@
+/*
+ * 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.search;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.core.VmRef;
+
+/**
+ *
+ */
+public class SearchBackendTest {
+
+    @Test
+    public void testSearchUnMatch() {
+
+        SearchBackend backend = new SearchBackend();
+        
+        HostRef host0 = new HostRef("0", "some weird host");
+        
+        boolean result = backend.match("some host", host0);
+        assertFalse(result);
+    }
+
+    @Test
+    public void testSearchMatch0() {
+
+        SearchBackend backend = new SearchBackend();
+        
+        HostRef host0 = new HostRef("0", "some host");
+        
+        backend.addHost(host0);
+        
+        boolean result = backend.match("some host", host0);
+        assertTrue(result);
+    }
+    
+    @Test
+    public void testSearchMatch1() {
+        SearchBackend backend = new SearchBackend();
+        
+        HostRef host0 = new HostRef("host0", "some weird host");
+        HostRef host1 = new HostRef("host1", "some host");
+
+        VmRef vm0 = new VmRef(host0, "vm0", 0, "some vm");
+        VmRef vm1 = new VmRef(host1, "vm1", 1, "some other vm");
+        
+        backend.addHost(host0);
+        backend.addHost(host1);
+        backend.addVM(vm0);
+        backend.addVM(vm1);
+        
+        boolean result = backend.match("some host", host1);
+        assertTrue(result);
+        
+        result = backend.match("some other vm", host1);
+        assertTrue(result);
+    }
+    
+    @Test
+    public void testSearchUnMatchWithParent() {
+
+        SearchBackend backend = new SearchBackend();
+        
+        HostRef host0 = new HostRef("host0", "some weird host");
+        HostRef host1 = new HostRef("host1", "some host");
+
+        VmRef vm0 = new VmRef(host0, "vm0", 0, "some vm");
+        VmRef vm1 = new VmRef(host1, "vm1", 1, "some other vm");
+        
+        backend.addHost(host0);
+        backend.addHost(host1);
+        backend.addVM(vm0);
+        backend.addVM(vm1);
+        
+        boolean result = backend.match("somehost", host1);
+        assertFalse(result);
+        
+        result = backend.match("some vm", host1);
+        assertFalse(result);
+    }
+    
+    @Test
+    public void testSearchMatch2() {
+        SearchBackend backend = new SearchBackend();
+        
+        HostRef host0 = new HostRef("host0", "some weird host");
+        HostRef host1 = new HostRef("host1", "some host");
+        HostRef host2 = new HostRef("host2", "some host");
+
+        VmRef vm0 = new VmRef(host0, "vm0", 0, "some vm");
+        VmRef vm1 = new VmRef(host1, "vm1", 1, "some other vm");
+        
+        backend.addHost(host0);
+        backend.addHost(host1);
+        backend.addVM(vm0);
+        backend.addVM(vm1);
+        
+        boolean result = backend.match("some host", host2);
+        assertTrue(result);
+        
+        result = backend.match("some other vm", host2);
+        assertFalse(result);
+    }
+}
--- a/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/vmlist/controller/HostTreeControllerTest.java	Thu Nov 14 18:17:50 2013 +0100
+++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/vmlist/controller/HostTreeControllerTest.java	Thu Nov 14 18:17:51 2013 +0100
@@ -196,7 +196,8 @@
         filter1.toggle();
         
         waitForSwing();
-        
+        waitForSwing();
+
         headers = proxyModel.getHeaders();
         assertEquals(2, headers.size());
         assertTrue(headers.contains(host0));
@@ -239,6 +240,7 @@
         filter2.toggle();
 
         waitForSwing();
+        waitForSwing();
 
         components  = proxyModel.getComponents(host0);
         assertEquals(1, components.size());
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/ObjectDetailsPanel.java	Thu Nov 14 18:17:50 2013 +0100
+++ b/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/ObjectDetailsPanel.java	Thu Nov 14 18:17:51 2013 +0100
@@ -57,7 +57,7 @@
 import com.redhat.thermostat.client.swing.EdtHelper;
 import com.redhat.thermostat.client.swing.SwingComponent;
 import com.redhat.thermostat.client.swing.components.SearchField;
-import com.redhat.thermostat.client.swing.components.SearchField.SearchAction;
+import com.redhat.thermostat.client.ui.SearchProvider.SearchAction;
 import com.redhat.thermostat.common.ActionListener;
 import com.redhat.thermostat.common.ActionEvent;
 import com.redhat.thermostat.common.ActionNotifier;
@@ -70,6 +70,7 @@
 import javax.swing.LayoutStyle.ComponentPlacement;
 import javax.swing.JScrollPane;
 import javax.swing.JTextPane;
+
 import java.awt.BorderLayout;
 import java.awt.Component;
 import java.awt.event.MouseAdapter;
@@ -173,11 +174,11 @@
                     .addContainerGap())
         );
 
-        searchField.addActionListener(new ActionListener<SearchAction>() {
+        searchField.addSearchListener(new ActionListener<SearchAction>() {
             @Override
             public void actionPerformed(ActionEvent<SearchAction> actionEvent) {
                 switch (actionEvent.getActionId()) {
-                case TEXT_CHANGED:
+                case PERFORM_SEARCH:
                     notifier.fireAction(ObjectAction.SEARCH);
                     break;
                 default: