changeset 789:a96af4716d10

Merge
author Roman Kennke <rkennke@redhat.com>
date Mon, 19 Nov 2012 11:40:08 +0100
parents 90903ba2673d (current diff) 7737fb7684b9 (diff)
children b063c6afa2d7
files eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/ChartUtils.java eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/HostRefViewPart.java eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/RefViewPart.java eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/ViewVisibilityWatcher.java eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/VmRefViewPart.java launcher/src/main/resources/META-INF/p2.inf
diffstat 96 files changed, 3609 insertions(+), 1260 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Mon Nov 19 11:35:17 2012 +0100
+++ b/Makefile	Mon Nov 19 11:40:08 2012 +0100
@@ -25,7 +25,7 @@
 # Default to cleaning the local repo and building core + eclipse
 # Cleaning the repo prevents things like not seeing build failures
 # after bundles have been renamed.
-all: clean-repo eclipse
+all: clean-repo eclipse eclipse-test
 
 core:
 	$(MAVEN) -f $(POM) $(MAVEN_FLAGS) $(MAVEN_SKIP_TEST) clean $(GOAL)
@@ -33,6 +33,16 @@
 core-install: create-repo-dir
 	$(MAVEN) -f $(POM) $(MAVEN_FLAGS) $(REPO_FLAG) $(MAVEN_SKIP_TEST) clean install
 
+eclipse-test: eclipse eclipse-test-p2
+ifeq ($(USE_VNC),true)
+	$(VNC) $(VNC_DISPLAY) $(VNC_FLAGS)
+endif
+	-$(MAVEN) -f eclipse/com.redhat.thermostat.eclipse.test/pom.xml $(MAVEN_FLAGS) $(REPO_FLAG) $(MAVEN_SKIP_TEST) clean $(GOAL)
+	-$(MAVEN) -f eclipse/com.redhat.thermostat.eclipse.test.ui/pom.xml $(MAVEN_FLAGS) $(REPO_FLAG) $(MAVEN_SKIP_TEST) clean $(GOAL)
+ifeq ($(USE_VNC),true)
+	$(VNC) -kill $(VNC_DISPLAY)
+endif
+
 eclipse-test-deps: core-install
 	$(MAVEN) -f eclipse/test-deps-bundle-wrapping/pom.xml $(MAVEN_FLAGS) $(REPO_FLAG) $(MAVEN_SKIP_TEST) clean install
 
@@ -45,14 +55,8 @@
 jfreechart-p2: jfreechart-deps
 	$(MAVEN) -f eclipse/jfreechart-p2-repository/pom.xml $(MAVEN_FLAGS) $(REPO_FLAG) $(MAVEN_SKIP_TEST) clean $(GOAL)
 
-eclipse: eclipse-test-p2 jfreechart-p2
-ifeq ($(USE_VNC),true)
-	$(VNC) $(VNC_DISPLAY) $(VNC_FLAGS)
-endif
-	-$(MAVEN) -f eclipse/pom.xml $(MAVEN_FLAGS) $(REPO_FLAG) $(MAVEN_SKIP_TEST) clean $(GOAL)
-ifeq ($(USE_VNC),true)
-	$(VNC) -kill $(VNC_DISPLAY)
-endif
+eclipse: jfreechart-p2
+	$(MAVEN) -f eclipse/pom.xml $(MAVEN_FLAGS) $(REPO_FLAG) $(MAVEN_SKIP_TEST) clean $(GOAL)
 
 create-repo-dir:
 	mkdir -p $(REPO_LOC)
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactoryImpl.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactoryImpl.java	Mon Nov 19 11:40:08 2012 +0100
@@ -52,7 +52,20 @@
 
     private final Storage storage;
     private final BundleContext bundleContext;
+    @SuppressWarnings("rawtypes")
     private final List<ServiceRegistration> registeredServices = new ArrayList<>();
+    private AgentInfoDAO agentDAO;
+    private BackendInfoDAO backendInfoDAO;
+    private HostInfoDAO hostInfoDAO;
+    private CpuStatDAO cpuStatDAO;
+    private MemoryStatDAO memoryStatDAO;
+    private NetworkInterfaceInfoDAO networkInfoDAO;
+    private VmInfoDAO vmInfoDAO;
+    private VmCpuStatDAO vmCpuStatDAO;
+    private VmClassStatDAO vmClassStatDAO;
+    private VmMemoryStatDAO vmMemStatDAO;
+    private VmGcStatDAO vmGcStatDAO;
+    private HeapDAO heapDAO;
 
     public DAOFactoryImpl(StorageProvider prov) {
         this(FrameworkUtil.getBundle(DAOFactoryImpl.class).getBundleContext(), prov);
@@ -70,66 +83,68 @@
 
     @Override
     public AgentInfoDAO getAgentInfoDAO() {
-        return new AgentInfoDAOImpl(storage);
+        ensureStorageConnected();
+        return agentDAO;
     }
 
     @Override
     public BackendInfoDAO getBackendInfoDAO() {
-        return new BackendInfoDAOImpl(storage);
+        ensureStorageConnected();
+        return backendInfoDAO;
     }
 
     @Override
     public HostInfoDAO getHostInfoDAO() {
         ensureStorageConnected();
-        return new HostInfoDAOImpl(storage, new AgentInfoDAOImpl(storage));
+        return hostInfoDAO;
     }
 
     @Override
     public CpuStatDAO getCpuStatDAO() {
         ensureStorageConnected();
-        return new CpuStatDAOImpl(storage);
+        return cpuStatDAO;
     }
 
     @Override
     public MemoryStatDAO getMemoryStatDAO() {
         ensureStorageConnected();
-        return new MemoryStatDAOImpl(storage);
+        return memoryStatDAO;
     }
 
     @Override
     public NetworkInterfaceInfoDAO getNetworkInterfaceInfoDAO() {
         ensureStorageConnected();
-        return new NetworkInterfaceInfoDAOImpl(storage);
+        return networkInfoDAO;
     }
 
     @Override
     public VmInfoDAO getVmInfoDAO() {
         ensureStorageConnected();
-        return new VmInfoDAOImpl(storage);
+        return vmInfoDAO;
     }
 
     @Override
     public VmCpuStatDAO getVmCpuStatDAO() {
         ensureStorageConnected();
-        return new VmCpuStatDAOImpl(storage);
+        return vmCpuStatDAO;
     }
 
     @Override
     public VmMemoryStatDAO getVmMemoryStatDAO() {
         ensureStorageConnected();
-        return new VmMemoryStatDAOImpl(storage);
+        return vmMemStatDAO;
     }
 
     @Override
     public VmClassStatDAO getVmClassStatsDAO() {
         ensureStorageConnected();
-        return new VmClassStatDAOImpl(storage);
+        return vmClassStatDAO;
     }
 
     @Override
     public VmGcStatDAO getVmGcStatDAO() {
         ensureStorageConnected();
-        return new VmGcStatDAOImpl(storage);
+        return vmGcStatDAO;
     }
 
     @Override
@@ -146,11 +161,13 @@
     @Override
     public HeapDAO getHeapDAO() {
         ensureStorageConnected();
-        return new HeapDAOImpl(storage);
+        return heapDAO;
     }
 
     @Override
     public void registerDAOsAndStorageAsOSGiServices() {
+        ensureStorageConnected();
+        createDAOs();
         registerAndRecordService(Storage.class, getStorage());
 
         registerAndRecordService(AgentInfoDAO.class, getAgentInfoDAO());
@@ -169,6 +186,24 @@
         registerAndRecordService(HeapDAO.class, getHeapDAO());
     }
 
+    /*
+     * Pre: Db connected
+     */
+    void createDAOs() {
+        agentDAO = new AgentInfoDAOImpl(storage);
+        backendInfoDAO = new BackendInfoDAOImpl(storage);
+        hostInfoDAO = new HostInfoDAOImpl(storage, agentDAO);
+        cpuStatDAO = new CpuStatDAOImpl(storage);
+        memoryStatDAO = new MemoryStatDAOImpl(storage);
+        networkInfoDAO = new NetworkInterfaceInfoDAOImpl(storage);
+        vmInfoDAO = new VmInfoDAOImpl(storage);
+        vmCpuStatDAO = new VmCpuStatDAOImpl(storage);
+        vmClassStatDAO = new VmClassStatDAOImpl(storage);
+        vmMemStatDAO = new VmMemoryStatDAOImpl(storage);
+        vmGcStatDAO = new VmGcStatDAOImpl(storage);
+        heapDAO = new HeapDAOImpl(storage);
+    }
+
     private <K> void registerAndRecordService(Class<K> serviceType, K serviceImplementation) {
         registeredServices.add(bundleContext.registerService(serviceType, serviceImplementation, null));
     }
--- a/common/core/src/test/java/com/redhat/thermostat/common/dao/MongoDAOFactoryTest.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/common/core/src/test/java/com/redhat/thermostat/common/dao/MongoDAOFactoryTest.java	Mon Nov 19 11:40:08 2012 +0100
@@ -57,8 +57,6 @@
     private Connection connection;
     private StorageProvider provider;
     private DAOFactory daoFactory;
-    private HostRef hostRef;
-    private VmRef vmRef;
 
     @Before
     public void setUp() {
@@ -70,9 +68,8 @@
         when(connection.isConnected()).thenReturn(true);
         provider = mock(StorageProvider.class);
         when(provider.createStorage()).thenReturn(storage);
-        hostRef = mock(HostRef.class);
-        vmRef = mock(VmRef.class);
         daoFactory = new DAOFactoryImpl(bundleContext, provider);
+        ((DAOFactoryImpl)daoFactory).createDAOs();
     }
 
     @Test
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/archetype-ext/pom.xml	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+
+ Copyright 2012 Red Hat, Inc.
+
+ This file is part of Thermostat.
+
+ Thermostat is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ Thermostat is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Thermostat; see the file COPYING.  If not see
+ <http://www.gnu.org/licenses />.
+
+ Linking this code with other modules is making a combined work
+ based on this code.  Thus, the terms and conditions of the GNU
+ General Public License cover the whole combination.
+
+ As a special exception, the copyright holders of this code give
+ you permission to link this code with independent modules to
+ produce an executable, regardless of the license terms of these
+ independent modules, and to copy and distribute the resulting
+ executable under terms of your choice, provided that you also
+ meet, for each linked independent module, the terms and conditions
+ of the license of that module.  An independent module is a module
+ which is not derived from or based on this code.  If you modify
+ this code, you may extend this exception to your version of the
+ library, but you are not obligated to do so.  If you do not wish
+ to do so, delete this exception statement from your version.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.redhat.thermostat</groupId>
+    <artifactId>thermostat-devel-modules</artifactId>
+    <version>0.5.0-SNAPSHOT</version>
+  </parent>
+
+  <groupId>com.redhat.thermostat</groupId>
+  <artifactId>thermostat-maven-archetype-ext</artifactId>
+  <version>0.5.0-SNAPSHOT</version>
+
+  <packaging>maven-archetype</packaging>
+
+  <name>Thermostat Development External Maven Archetype</name>
+
+  <build>
+    <extensions>
+      <extension>
+        <groupId>org.apache.maven.archetype</groupId>
+        <artifactId>archetype-packaging</artifactId>
+        <version>2.2</version>
+      </extension>
+    </extensions>
+
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <artifactId>maven-archetype-plugin</artifactId>
+          <version>2.2</version>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+  </build> 
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/archetype-ext/src/main/resources/META-INF/maven/archetype-metadata.xml	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,32 @@
+<archetype-descriptor xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0 http://maven.apache.org/xsd/archetype-descriptor-1.0.0.xsd"
+  name="Thermostat Extension Maven Archetype">
+  <requiredProperties>
+    <requiredProperty key="bundleSymbolicName">
+      <defaultValue>${package}.exampleBundleSymbolicName-core</defaultValue>
+    </requiredProperty>
+    <requiredProperty key="moduleName">
+      <defaultValue>The name of your Thermostat extension goes here</defaultValue>
+    </requiredProperty>
+    <requiredProperty key="thermostatVersion">
+      <defaultValue>0.5.0-SNAPSHOT</defaultValue>
+    </requiredProperty>
+  </requiredProperties>
+
+  <fileSets>
+    <fileSet filtered="true" packaged="true" encoding="UTF-8" >
+      <directory>src/main/java</directory>
+      <includes>
+	<include>internal/Activator.java</include>
+	<include>ExampleCommand.java</include>
+      </includes>
+    </fileSet>
+    <fileSet filtered="true" packaged="true" encoding="UTF-8" >
+      <directory>src/test/java</directory>
+      <includes>
+	<include>internal/ActivatorTest.java</include>
+	<include>ExampleCommandTest.java</include>
+      </includes>
+    </fileSet>
+  </fileSets>
+</archetype-descriptor>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/archetype-ext/src/main/resources/archetype-resources/pom.xml	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,66 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>${groupId}</groupId>
+  <artifactId>${artifactId}</artifactId>
+  <version>${version}</version>
+  <!-- Maven bundle plugin provides this packaging type -->
+  <packaging>bundle</packaging>
+
+  <name>${moduleName}</name>
+  <!-- URL of your organization -->
+  <url>http://www.example.com</url>
+
+  <dependencies>
+    <!--
+         This may need adjustment. Make sure you depend on the most
+         suitable thermostat artifact here. You will know best :)
+    -->
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-core</artifactId>
+      <version>${thermostatVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.10</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <version>1.9.0</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <version>2.3.7</version>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <!-- This should be unique. Please use reverse URL notation. -->
+            <Bundle-SymbolicName>${bundleSymbolicName}</Bundle-SymbolicName>
+            <Bundle-Vendor>Foo Bar, Inc.</Bundle-Vendor>
+            <Bundle-Activator>${package}.internal.Activator</Bundle-Activator>
+            <Export-Package>
+              ${package}
+            </Export-Package>
+            <Private-Package>
+              ${package}.internal
+            </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+
+  </build>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/archetype-ext/src/main/resources/archetype-resources/src/main/java/ExampleCommand.java	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,29 @@
+package ${package};
+
+import com.redhat.thermostat.common.cli.CommandContext;
+import com.redhat.thermostat.common.cli.CommandException;
+import com.redhat.thermostat.common.cli.SimpleCommand;
+
+public class ExampleCommand extends SimpleCommand {
+
+    public ExampleCommand() {
+        // Nothing
+    }
+    
+    @Override
+    public void run(CommandContext ctx) throws CommandException {
+        // FIXME: Do something useful :)
+        ctx.getConsole().getOutput().println("Hello World!");
+    }
+
+    @Override
+    public String getName() {
+        return "examplecommand";
+    }
+
+    @Override
+    public boolean isStorageRequired() {
+        return false;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/archetype-ext/src/main/resources/archetype-resources/src/main/java/internal/Activator.java	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,20 @@
+package ${package}.internal;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Stub Activator
+ */
+public class Activator implements BundleActivator {
+
+    @Override
+    public void start(BundleContext context) throws Exception {    
+        // Activate your bundle here
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        // unregister services here
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/archetype-ext/src/main/resources/archetype-resources/src/test/java/ExampleCommandTest.java	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,43 @@
+package ${package};
+
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+import com.redhat.thermostat.common.cli.CommandException;
+import com.redhat.thermostat.common.cli.SimpleArguments;
+import com.redhat.thermostat.test.TestCommandContextFactory;
+
+public class ExampleCommandTest {
+
+    private TestCommandContextFactory cmdCtxFactory;
+    private BundleContext bundleContext;
+    private ExampleCommand cmd;
+    
+    @Before
+    public void setUp() {
+        Bundle sysBundle = mock(Bundle.class);
+        bundleContext = mock(BundleContext.class);
+        when(bundleContext.getBundle(0)).thenReturn(sysBundle);
+        cmdCtxFactory = new TestCommandContextFactory(bundleContext);
+        cmd = new ExampleCommand();
+    }
+    
+    @After
+    public void tearDown() {
+        cmdCtxFactory = null;
+        cmd = null;
+    }
+    
+    @Test
+    public void verifyCommandOutput() throws Exception {
+        // TODO: Show something more useful
+        fail("Implement Me!");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/archetype-ext/src/main/resources/archetype-resources/src/test/java/internal/ActivatorTest.java	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,6 @@
+package ${package}.internal;
+
+public class ActivatorTest {
+
+   // TODO: Implement
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/pom.xml	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+
+ Copyright 2012 Red Hat, Inc.
+
+ This file is part of Thermostat.
+
+ Thermostat is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ Thermostat is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Thermostat; see the file COPYING.  If not see
+ <http://www.gnu.org/licenses />.
+
+ Linking this code with other modules is making a combined work
+ based on this code.  Thus, the terms and conditions of the GNU
+ General Public License cover the whole combination.
+
+ As a special exception, the copyright holders of this code give
+ you permission to link this code with independent modules to
+ produce an executable, regardless of the license terms of these
+ independent modules, and to copy and distribute the resulting
+ executable under terms of your choice, provided that you also
+ meet, for each linked independent module, the terms and conditions
+ of the license of that module.  An independent module is a module
+ which is not derived from or based on this code.  If you modify
+ this code, you may extend this exception to your version of the
+ library, but you are not obligated to do so.  If you do not wish
+ to do so, delete this exception statement from your version.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.redhat.thermostat</groupId>
+    <artifactId>thermostat</artifactId>
+    <version>0.5.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>thermostat-devel-modules</artifactId>
+  <packaging>pom</packaging>
+
+  <name>Thermostat Development Modules</name>
+
+  <modules>
+    <module>archetype-ext</module>
+  </modules>
+
+</project>
--- a/distribution/config/commands/service.properties	Mon Nov 19 11:35:17 2012 +0100
+++ b/distribution/config/commands/service.properties	Mon Nov 19 11:40:08 2012 +0100
@@ -1,27 +1,9 @@
 bundles = thermostat-agent-core-@project.version@.jar, \
-          thermostat-web-common-@project.version@.jar, \
-          thermostat-web-client-@project.version@.jar, \
           thermostat-osgi-process-handler-@project.version@.jar, \
-          thermostat-common-core-@project.version@.jar, \
           thermostat-common-command-@project.version@.jar, \
           thermostat-agent-command-@project.version@.jar, \
           thermostat-agent-cli-@project.version@.jar, \
           thermostat-thread-collector-@project.version@.jar, \
-          commons-fileupload-@fileupload.version@.jar, \
-          commons-io-@commons-io.version@.jar, \
-          httpcore-osgi-@httpcomponents.version@.jar, \
-          httpclient-osgi-@httpcomponents.version@.jar, \
-          gson.jar, \
-          jetty-continuation.jar, \
-          jetty-http.jar, \
-          jetty-io.jar, \
-          jetty-security.jar, \
-          jetty-server.jar, \
-          jetty-servlet.jar, \
-          jetty-util.jar, \
-          jetty-webapp.jar, \
-          jetty-xml.jar, \
-          javax-servlet.jar, \
           netty.jar
 
 description = starts and stops the thermostat storage and agent
--- a/distribution/config/commands/webservice.properties	Mon Nov 19 11:35:17 2012 +0100
+++ b/distribution/config/commands/webservice.properties	Mon Nov 19 11:40:08 2012 +0100
@@ -20,7 +20,7 @@
 
 usage = thermostat webservice
 
-options = storageURL, username, password, port
+options = storageURL, username, password, bindAddrs
 
 storageURL.short = u
 storageURL.long = storageURL
@@ -38,8 +38,9 @@
 password.required = false
 password.description = the password to authenticate against the storage
 
-port.short = p
-port.long = port
-port.hasarg = true
-port.required = true
-port.description = the TCP port on which the web service will be started
+bindAddrs.long = bindAddrs
+bindAddrs.short = b
+bindAddrs.hasarg = true
+bindAddrs.required = true
+bindAddrs.description = A comma separated list of IP address, \
+port pairs used for binding (e.g. 127.0.0.1:8888,127.0.0.1:8889)
--- a/eclipse/README.building	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/README.building	Mon Nov 19 11:40:08 2012 +0100
@@ -1,9 +1,14 @@
 # perhaps set -Dmaven.repo.local=/desired/path
 # for all of mvn commands below.
 cd path/to/thermostat/home
+# Build core Thermostat
 mvn clean install
+# Build Eclipse client
 mvn -f eclipse/jfreechart-bundle-wrapping/pom.xml clean install
 mvn -f eclipse/jfreechart-p2-repository/pom.xml clean package
+mvn -f eclipse/pom.xml clean package
+# Build and run Eclipse client tests
 mvn -f eclipse/test-deps-bundle-wrapping/pom.xml clean install
 mvn -f eclipse/test-deps-p2-repository/pom.xml clean package
-mvn -f eclipse/pom.xml clean package
+mvn -f eclipse/com.redhat.thermostat.eclipse.test/pom.xml clean package
+mvn -f eclipse/com.redhat.thermostat.eclipse.test.ui/pom.xml clean package
--- a/eclipse/com.redhat.thermostat.client.feature/feature.xml	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.client.feature/feature.xml	Mon Nov 19 11:40:08 2012 +0100
@@ -99,6 +99,13 @@
          unpack="false"/>
 
    <plugin
+         id="com.redhat.thermostat.storage.core"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
          id="org.apache.servicemix.bundles.lucene"
          download-size="0"
          install-size="0"
@@ -112,4 +119,11 @@
          version="0.0.0"
          unpack="false"/>
 
+   <plugin
+         id="com.redhat.thermostat.client.classstat.core"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
 </feature>
--- a/eclipse/com.redhat.thermostat.client.feature/pom.xml	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.client.feature/pom.xml	Mon Nov 19 11:40:08 2012 +0100
@@ -21,6 +21,11 @@
     </dependency>
     <dependency>
       <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-client-vmclassstat-core</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
       <artifactId>thermostat-web-client</artifactId>
       <version>0.5.0-SNAPSHOT</version>
     </dependency>
@@ -29,6 +34,11 @@
       <artifactId>thermostat-web-common</artifactId>
       <version>0.5.0-SNAPSHOT</version>
     </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-storage-core</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
+    </dependency>
   </dependencies>
 
   <build>
--- a/eclipse/com.redhat.thermostat.eclipse.chart.common/META-INF/MANIFEST.MF	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.common/META-INF/MANIFEST.MF	Mon Nov 19 11:40:08 2012 +0100
@@ -19,6 +19,7 @@
  com.redhat.thermostat.storage.core,
  com.redhat.thermostat.common.utils,
  com.redhat.thermostat.eclipse,
+ com.redhat.thermostat.eclipse.views,
  org.jfree.chart,
  org.jfree.chart.axis,
  org.jfree.chart.event,
@@ -32,5 +33,6 @@
  org.osgi.framework;version="1.3.0"
 Bundle-ActivationPolicy: lazy
 Require-Bundle: org.eclipse.ui,
- org.eclipse.core.runtime;bundle-version="3.8.0"
+ org.eclipse.core.runtime;bundle-version="3.8.0",
+ com.redhat.thermostat.eclipse;bundle-version="0.5.0"
 Export-Package: com.redhat.thermostat.eclipse.chart.common
--- a/eclipse/com.redhat.thermostat.eclipse.chart.common/pom.xml	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.common/pom.xml	Mon Nov 19 11:40:08 2012 +0100
@@ -17,15 +17,9 @@
   <dependencies>
     <dependency>
       <groupId>com.redhat.thermostat.eclipse.parent</groupId>
-      <artifactId>com.redhat.thermostat.eclipse</artifactId>
+      <artifactId>com.redhat.thermostat.client.feature</artifactId>
       <version>0.5.0-SNAPSHOT</version>
     </dependency>
-    <dependency>
-      <groupId>com.redhat.thermostat.eclipse.parent</groupId>
-      <artifactId>com.redhat.thermostat.eclipse.jfreechart-repo</artifactId>
-      <version>0.5.0-SNAPSHOT</version>
-      <type>pom</type>
-    </dependency>
   </dependencies>
 
 </project>
--- a/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/ChartUtils.java	Mon Nov 19 11:35:17 2012 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <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.eclipse.chart.common;
-
-import java.util.concurrent.CountDownLatch;
-
-import org.eclipse.ui.PlatformUI;
-
-public class ChartUtils {
-    
-    /**
-     * Executes runnable in the SWT UI thread after the necessary
-     * controls are created.
-     * @param latch - blocks runnable until count is zero
-     * @param runnable - task to execute
-     */
-    public static void runAfterCreated(CountDownLatch latch, Runnable runnable) {
-        try {
-            latch.await();
-            PlatformUI.getWorkbench().getDisplay().syncExec(runnable);
-        } catch (InterruptedException e) {
-            // Restore interrupted status
-            Thread.currentThread().interrupt();
-        }
-    }
-
-}
--- a/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/HostCpuViewPart.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/HostCpuViewPart.java	Mon Nov 19 11:40:08 2012 +0100
@@ -36,6 +36,8 @@
 
 package com.redhat.thermostat.eclipse.chart.common;
 
+import org.eclipse.swt.widgets.Composite;
+
 import com.redhat.thermostat.client.core.views.HostCpuViewProvider;
 import com.redhat.thermostat.client.ui.HostCpuController;
 import com.redhat.thermostat.common.dao.CpuStatDAO;
@@ -43,23 +45,24 @@
 import com.redhat.thermostat.common.dao.HostRef;
 import com.redhat.thermostat.common.utils.OSGIUtils;
 import com.redhat.thermostat.eclipse.SWTComponent;
+import com.redhat.thermostat.eclipse.views.HostRefViewPart;
 
 public class HostCpuViewPart extends HostRefViewPart {
 
-    private HostCpuController cpuController;
-
     @Override
-    protected void createControllerView(HostRef ref) {
+    protected SWTComponent createControllerView(HostRef ref, Composite parent) {
         HostInfoDAO hostInfoDao = OSGIUtils.getInstance().getService(
                 HostInfoDAO.class);
         CpuStatDAO cpuStatDao = OSGIUtils.getInstance().getService(
                 CpuStatDAO.class);
-        HostCpuViewProvider viewProvider = OSGIUtils.getInstance().getService(
-                HostCpuViewProvider.class);
-        cpuController = createController(hostInfoDao, cpuStatDao, ref,
-                viewProvider);
+        SWTHostCpuViewProvider viewProvider = (SWTHostCpuViewProvider) OSGIUtils
+                .getInstance().getService(HostCpuViewProvider.class);
+        viewProvider.setParent(parent);
+
+        HostCpuController cpuController = createController(hostInfoDao,
+                cpuStatDao, ref, viewProvider);
         SWTComponent view = (SWTComponent) cpuController.getView();
-        view.createControl(top);
+        return view;
     }
 
     public HostCpuController createController(HostInfoDAO hostInfoDao,
--- a/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/HostMemoryViewPart.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/HostMemoryViewPart.java	Mon Nov 19 11:40:08 2012 +0100
@@ -36,6 +36,8 @@
 
 package com.redhat.thermostat.eclipse.chart.common;
 
+import org.eclipse.swt.widgets.Composite;
+
 import com.redhat.thermostat.client.core.views.HostMemoryViewProvider;
 import com.redhat.thermostat.client.ui.HostMemoryController;
 import com.redhat.thermostat.common.dao.HostInfoDAO;
@@ -43,23 +45,23 @@
 import com.redhat.thermostat.common.dao.MemoryStatDAO;
 import com.redhat.thermostat.common.utils.OSGIUtils;
 import com.redhat.thermostat.eclipse.SWTComponent;
+import com.redhat.thermostat.eclipse.views.HostRefViewPart;
 
 public class HostMemoryViewPart extends HostRefViewPart {
 
-    private HostMemoryController controller;
-
     @Override
-    protected void createControllerView(HostRef ref) {
+    protected SWTComponent createControllerView(HostRef ref, Composite parent) {
         HostInfoDAO hostInfoDao = OSGIUtils.getInstance().getService(
                 HostInfoDAO.class);
         MemoryStatDAO memoryStatDao = OSGIUtils.getInstance().getService(
                 MemoryStatDAO.class);
-        HostMemoryViewProvider viewProvider = OSGIUtils.getInstance()
-                .getService(HostMemoryViewProvider.class);
-        controller = createController(hostInfoDao, memoryStatDao, ref,
-                viewProvider);
+        SWTHostMemoryViewProvider viewProvider = (SWTHostMemoryViewProvider) OSGIUtils
+                .getInstance().getService(HostMemoryViewProvider.class);
+        viewProvider.setParent(parent);
+        HostMemoryController controller = createController(hostInfoDao,
+                memoryStatDao, ref, viewProvider);
         SWTComponent view = (SWTComponent) controller.getView();
-        view.createControl(top);
+        return view;
     }
 
     public HostMemoryController createController(HostInfoDAO hostInfoDao,
--- a/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/HostRefViewPart.java	Mon Nov 19 11:35:17 2012 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <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.eclipse.chart.common;
-
-import com.redhat.thermostat.common.dao.HostRef;
-import com.redhat.thermostat.common.dao.VmRef;
-
-public abstract class HostRefViewPart extends RefViewPart<HostRef> {
-
-    public HostRefViewPart() {
-        super();
-    }
-
-    @Override
-    protected HostRef getRefFromSelection(Object selection) {
-        HostRef ref = null;
-        if (selection instanceof HostRef) {
-            ref = (HostRef) selection;
-        }
-        else if (selection instanceof VmRef) {
-            ref = ((VmRef) selection).getAgent();
-        }
-        return ref;
-    }
-
-    @Override
-    protected String getNoSelectionMessage() {
-        return "No host selected";
-    }
-
-}
\ No newline at end of file
--- a/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/RefViewPart.java	Mon Nov 19 11:35:17 2012 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,146 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <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.eclipse.chart.common;
-
-import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.ui.ISelectionListener;
-import org.eclipse.ui.IViewPart;
-import org.eclipse.ui.IWorkbenchPart;
-import org.eclipse.ui.IWorkbenchWindow;
-import org.eclipse.ui.PlatformUI;
-import org.eclipse.ui.part.ViewPart;
-
-import com.redhat.thermostat.common.dao.Ref;
-import com.redhat.thermostat.eclipse.ThermostatConstants;
-
-public abstract class RefViewPart<T extends Ref> extends ViewPart implements ISelectionListener {
-
-    protected Composite top;
-
-    private Composite parent;
-    private Object selectedElement;
-
-    public RefViewPart() {
-        super();
-    }
-
-    @Override
-    public void createPartControl(Composite parent) {
-        this.parent = parent;
-        
-        createComposite();
-        
-        getWorkbenchWindow().getSelectionService().addSelectionListener(this);
-        
-        // Check for an existing selection
-        boolean selected = false;
-        IViewPart part = getWorkbenchWindow().getActivePage().findView(ThermostatConstants.VIEW_ID_HOST_VM);
-        if (part != null) {
-            ISelection selection = part.getSite().getSelectionProvider().getSelection();
-            if (selection instanceof IStructuredSelection) {
-                handleSelection(selection);
-                selected = true;
-            }
-        }
-        if (!selected) {
-            createNoSelectionLabel();
-        }
-    }
-
-    public void createNoSelectionLabel() {
-        Label noHost = new Label(top, SWT.NONE);
-        noHost.setText(getNoSelectionMessage());
-    }
-
-    public IWorkbenchWindow getWorkbenchWindow() {
-        return PlatformUI.getWorkbench().getActiveWorkbenchWindow();
-    }
-    
-    @Override
-    public void setFocus() {
-        parent.setFocus();
-    }
-
-    protected abstract void createControllerView(T ref);
-    
-    protected abstract T getRefFromSelection(Object selection);
-    
-    protected abstract String getNoSelectionMessage();
-    
-    @Override
-    public void selectionChanged(IWorkbenchPart part, ISelection selection) {
-        // We must have received createPartControl
-        if (parent != null && !parent.isDisposed()) {
-            // Check if a HostRef has been selected
-            if (part.getSite().getId().equals(ThermostatConstants.VIEW_ID_HOST_VM)) {
-                if (selection instanceof IStructuredSelection) {
-                    handleSelection(selection);
-                }
-            }
-        }
-    }
-
-    private void handleSelection(ISelection selection) {
-        Object previous = selectedElement;
-        selectedElement = ((IStructuredSelection) selection).getFirstElement();
-        if (selectedElement != previous) {
-            T ref = getRefFromSelection(selectedElement);
-            if (ref != null) {
-                // Replace the existing view
-                top.dispose();
-                createComposite();
-   
-                createControllerView(ref);
-   
-                parent.layout();
-            }
-        }
-    }
-
-    private void createComposite() {
-        top = new Composite(parent, SWT.NONE);
-        top.setLayout(new GridLayout());
-        top.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-    }
-
-}
--- a/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/SWTHostCpuView.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/SWTHostCpuView.java	Mon Nov 19 11:40:08 2012 +0100
@@ -43,7 +43,6 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.CountDownLatch;
 
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.DisposeEvent;
@@ -91,20 +90,13 @@
     private Composite chartTop;
     private Composite legendTop;
     private Composite parent;
-    private ViewVisibilityWatcher watcher;
-    private CountDownLatch latch;
     
-    public SWTHostCpuView() {
+    public SWTHostCpuView(Composite parent) {
+        this.parent = parent;
         datasetCollection = new TimeSeriesCollection();
         datasets = new HashMap<Integer, TimeSeries>();
         colors = new HashMap<String, Color>();
-        watcher = new ViewVisibilityWatcher(notifier);
         chart = createCpuChart();
-        latch = new CountDownLatch(1);
-    }
-    
-    public void createControl(Composite parent) {
-        this.parent = parent;
         
         Label summaryLabel = new Label(parent, SWT.LEAD);
         Font stdFont = summaryLabel.getFont();
@@ -117,7 +109,7 @@
         
         Composite detailsTop = new Composite(parent, SWT.NONE);
         detailsTop.setLayout(new GridLayout(3, false));
-
+        
         Label cpuModelLabel = new Label(detailsTop, SWT.TRAIL);
         cpuModelLabel.setText(translator.localize(LocaleResources.HOST_INFO_CPU_MODEL));
         GridData hIndentLayoutData = new GridData();
@@ -130,7 +122,7 @@
         cpuModel = new Label(detailsTop, SWT.LEAD);
         cpuModel.setData(ThermostatConstants.TEST_TAG, TEST_ID_CPU_MODEL);
         cpuModel.setText("Unknown");
-
+        
         Label cpuCountLabel = new Label(detailsTop, SWT.TRAIL);
         cpuCountLabel.setText(translator.localize(LocaleResources.HOST_INFO_CPU_COUNT));
         cpuCountLabel.setLayoutData(hIndentLayoutData);
@@ -152,14 +144,8 @@
         legendLayout.wrap = false;
         legendLayout.marginHeight = 0;
         legendTop.setLayout(legendLayout);
-        
-        // Notify threads that controls are created
-        latch.countDown();
-        
-        // Don't start giving updates until controls are created
-        watcher.watch(parent, ThermostatConstants.VIEW_ID_HOST_CPU);
     }
-
+    
     private JFreeChart createCpuChart() {
         JFreeChart chart = ChartFactory.createTimeSeriesChart(null,
                 translator.localize(LocaleResources.HOST_CPU_USAGE_CHART_TIME_LABEL),
@@ -295,9 +281,7 @@
     }
     
     private void addLegendItem(final String humanReadableName, final Color color) {
-        // We need to wait for the controls to be fully constructed
-        // before modifying the legend
-        ChartUtils.runAfterCreated(latch, new Runnable() {
+        PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
             @Override
             public void run() {
                 createLabelWithLegend(legendTop, humanReadableName,
@@ -310,5 +294,15 @@
     public JFreeChart getChart() {
         return chart;
     }
+
+    @Override
+    public void show() {
+        notifier.fireAction(Action.VISIBLE);
+    }
+
+    @Override
+    public void hide() {
+        notifier.fireAction(Action.HIDDEN);
+    }
     
 }
--- a/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/SWTHostCpuViewProvider.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/SWTHostCpuViewProvider.java	Mon Nov 19 11:40:08 2012 +0100
@@ -38,12 +38,14 @@
 
 import com.redhat.thermostat.client.core.views.HostCpuView;
 import com.redhat.thermostat.client.core.views.HostCpuViewProvider;
+import com.redhat.thermostat.eclipse.SWTViewProvider;
 
-public class SWTHostCpuViewProvider implements HostCpuViewProvider {
+public class SWTHostCpuViewProvider extends SWTViewProvider implements
+        HostCpuViewProvider {
 
     @Override
     public HostCpuView createView() {
-        return new SWTHostCpuView();
+        return new SWTHostCpuView(getParent());
     }
 
 }
--- a/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/SWTHostMemoryView.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/SWTHostMemoryView.java	Mon Nov 19 11:40:08 2012 +0100
@@ -44,7 +44,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.CountDownLatch;
 
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.DisposeEvent;
@@ -93,27 +92,20 @@
     private final Map<String, Color> colors;
     private final CopyOnWriteArrayList<GraphVisibilityChangeListener> listeners;
     private final Map<String, Composite> checkboxes;
-    private final CountDownLatch latch;
     
     private Composite parent;
     private Label totalMemory;
-    private ViewVisibilityWatcher watcher;
     private JFreeChart chart;
     private Composite legendTop;
     
-    public SWTHostMemoryView() {
+    public SWTHostMemoryView(Composite parent) {
+        this.parent = parent;
         this.memoryCollection = new TimeSeriesCollection();
         this.dataset = Collections.synchronizedMap(new HashMap<String, TimeSeries>());
         this.colors = new HashMap<String, Color>();
         this.listeners = new CopyOnWriteArrayList<GraphVisibilityChangeListener>();
         this.checkboxes = new HashMap<String, Composite>();
-        this.watcher = new ViewVisibilityWatcher(notifier);
-        this.latch = new CountDownLatch(1);
         this.chart = createMemoryChart();
-    }
-    
-    public void createControl(Composite parent) {
-        this.parent = parent;
         
         Label summaryLabel = new Label(parent, SWT.LEAD);
         Font stdFont = summaryLabel.getFont();
@@ -150,14 +142,8 @@
         legendLayout.wrap = false;
         legendLayout.marginHeight = 0;
         legendTop.setLayout(legendLayout);
-        
-        // Notify threads that controls are created
-        latch.countDown();
-        
-        // Don't start giving updates until controls are created
-        watcher.watch(parent, ThermostatConstants.VIEW_ID_HOST_MEMORY);
     }
-
+    
     @Override
     public void setTotalMemory(final String newValue) {
         PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
@@ -363,9 +349,7 @@
     }
 
     private void addLegendItem(final String tag, final String humanReadableName) {
-        // We need to wait for the controls to be fully constructed
-        // before modifying the legend
-        ChartUtils.runAfterCreated(latch, new Runnable() {
+        PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
             @Override
             public void run() {
                 Composite checkbox = createLabelWithLegend(legendTop,
@@ -377,9 +361,7 @@
     }
 
     private void removeLegendItem(final String tag) {
-        // We need to wait for the controls to be fully constructed
-        // before modifying the legend
-        ChartUtils.runAfterCreated(latch, new Runnable() {
+        PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
             @Override
             public void run() {
                 Composite checkbox = checkboxes.remove(tag);
@@ -397,4 +379,14 @@
         return dataset.get(tag);
     }
 
+    @Override
+    public void show() {
+        notifier.fireAction(Action.VISIBLE);
+    }
+
+    @Override
+    public void hide() {
+        notifier.fireAction(Action.HIDDEN);
+    }
+
 }
--- a/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/SWTHostMemoryViewProvider.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/SWTHostMemoryViewProvider.java	Mon Nov 19 11:40:08 2012 +0100
@@ -38,12 +38,14 @@
 
 import com.redhat.thermostat.client.core.views.HostMemoryView;
 import com.redhat.thermostat.client.core.views.HostMemoryViewProvider;
+import com.redhat.thermostat.eclipse.SWTViewProvider;
 
-public class SWTHostMemoryViewProvider implements HostMemoryViewProvider {
+public class SWTHostMemoryViewProvider extends SWTViewProvider implements
+        HostMemoryViewProvider {
 
     @Override
     public HostMemoryView createView() {
-        return new SWTHostMemoryView();
+        return new SWTHostMemoryView(getParent());
     }
 
 }
--- a/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/SWTVmCpuView.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/SWTVmCpuView.java	Mon Nov 19 11:40:08 2012 +0100
@@ -55,7 +55,6 @@
 import com.redhat.thermostat.client.locale.LocaleResources;
 import com.redhat.thermostat.common.locale.Translate;
 import com.redhat.thermostat.eclipse.SWTComponent;
-import com.redhat.thermostat.eclipse.ThermostatConstants;
 import com.redhat.thermostat.storage.model.DiscreteTimeData;
 
 public class SWTVmCpuView extends VmCpuView implements SWTComponent {
@@ -66,25 +65,19 @@
     private final TimeSeries cpuTimeSeries;
     
     private JFreeChart chart;
-    private ViewVisibilityWatcher watcher;
     
-    public SWTVmCpuView() {
+    public SWTVmCpuView(Composite parent) {
         data = new TimeSeriesCollection();
         cpuTimeSeries = new TimeSeries("cpu-stats");
-        watcher = new ViewVisibilityWatcher(notifier);
         chart = createCpuChart();
         
         data.addSeries(cpuTimeSeries);
-    }
-    
-    public void createControl(Composite parent) {
+        
         Composite chartTop = new RecentTimeSeriesChartComposite(parent, SWT.NONE, chart);
         chartTop.setLayout(new GridLayout());
         chartTop.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-        
-        watcher.watch(parent, ThermostatConstants.VIEW_ID_VM_CPU);
     }
-
+    
     private JFreeChart createCpuChart() {
         JFreeChart chart = ChartFactory.createTimeSeriesChart(
                 null,
@@ -128,5 +121,15 @@
     public JFreeChart getChart() {
         return chart;
     }
+
+    @Override
+    public void show() {
+        notifier.fireAction(Action.VISIBLE);
+    }
+
+    @Override
+    public void hide() {
+        notifier.fireAction(Action.HIDDEN);
+    }
     
 }
--- a/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/SWTVmCpuViewProvider.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/SWTVmCpuViewProvider.java	Mon Nov 19 11:40:08 2012 +0100
@@ -38,12 +38,14 @@
 
 import com.redhat.thermostat.client.core.views.VmCpuView;
 import com.redhat.thermostat.client.core.views.VmCpuViewProvider;
+import com.redhat.thermostat.eclipse.SWTViewProvider;
 
-public class SWTVmCpuViewProvider implements VmCpuViewProvider {
+public class SWTVmCpuViewProvider extends SWTViewProvider implements
+        VmCpuViewProvider {
 
     @Override
     public VmCpuView createView() {
-        return new SWTVmCpuView();
+        return new SWTVmCpuView(getParent());
     }
 
 }
--- a/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/SWTVmGcView.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/SWTVmGcView.java	Mon Nov 19 11:40:08 2012 +0100
@@ -42,7 +42,6 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.CountDownLatch;
 
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.graphics.Font;
@@ -50,6 +49,7 @@
 import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.PlatformUI;
 import org.jfree.chart.ChartFactory;
 import org.jfree.chart.JFreeChart;
 import org.jfree.chart.axis.DateAxis;
@@ -68,7 +68,6 @@
 import com.redhat.thermostat.client.ui.SampledDataset;
 import com.redhat.thermostat.common.locale.Translate;
 import com.redhat.thermostat.eclipse.SWTComponent;
-import com.redhat.thermostat.eclipse.ThermostatConstants;
 import com.redhat.thermostat.storage.model.IntervalTimeData;
 
 public class SWTVmGcView extends VmGcView implements SWTComponent {
@@ -79,30 +78,16 @@
     private final Map<String, Composite> subPanels;
     private final Map<String, JFreeChart> charts;
     
-    private ViewVisibilityWatcher watcher;
     private Composite parent;
-    private final CountDownLatch latch;
 
-    public SWTVmGcView() {
+    public SWTVmGcView(Composite parent) {
+        this.parent = parent;
         dataset = new HashMap<String, SampledDataset>();
         subPanels = Collections.synchronizedMap(new HashMap<String, Composite>());
-        watcher = new ViewVisibilityWatcher(notifier);
-        latch = new CountDownLatch(1);
         charts = new HashMap<String, JFreeChart>();
     }
 
     @Override
-    public void createControl(Composite parent) {
-        this.parent = parent;
-        
-        // Notify threads that controls are created
-        latch.countDown();
-        
-        // Don't start giving updates until controls are created
-        watcher.watch(parent, ThermostatConstants.VIEW_ID_VM_GC);
-    }
-
-    @Override
     public void addChart(final String tag, final String title, final String units) {
         EventQueue.invokeLater(new Runnable() {
             @Override
@@ -176,9 +161,7 @@
         // An array so we can modify it in the UI thread
         final Composite detailsTop[] = new Composite[1];
         
-        // We need to wait for the controls to be fully constructed
-        // before adding the chart to the composite
-        ChartUtils.runAfterCreated(latch, new Runnable() {
+        PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
 
             @Override
             public void run() {
@@ -248,13 +231,11 @@
     }
 
     private void destroyChartComposite(final Composite top) {
-        // We need to wait for the controls to be fully constructed
-        // before removing the chart from the composite
-        ChartUtils.runAfterCreated(latch, new Runnable() {
+        PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
 
             @Override
             public void run() {
-                if (!parent.isDisposed()) {
+                if (top != null && !top.isDisposed()) {
                     top.dispose();
                     parent.layout();
                 }
@@ -266,4 +247,14 @@
         return charts.get(tag);
     }
 
+    @Override
+    public void show() {
+        notifier.fireAction(Action.VISIBLE);
+    }
+
+    @Override
+    public void hide() {
+        notifier.fireAction(Action.HIDDEN);
+    }
+
 }
--- a/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/SWTVmGcViewProvider.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/SWTVmGcViewProvider.java	Mon Nov 19 11:40:08 2012 +0100
@@ -38,12 +38,14 @@
 
 import com.redhat.thermostat.client.core.views.VmGcView;
 import com.redhat.thermostat.client.core.views.VmGcViewProvider;
+import com.redhat.thermostat.eclipse.SWTViewProvider;
 
-public class SWTVmGcViewProvider implements VmGcViewProvider {
+public class SWTVmGcViewProvider extends SWTViewProvider implements
+        VmGcViewProvider {
 
     @Override
     public VmGcView createView() {
-        return new SWTVmGcView();
+        return new SWTVmGcView(getParent());
     }
 
 }
--- a/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/ViewVisibilityWatcher.java	Mon Nov 19 11:35:17 2012 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,137 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <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.eclipse.chart.common;
-
-import org.eclipse.swt.events.DisposeEvent;
-import org.eclipse.swt.events.DisposeListener;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.ui.IPartListener2;
-import org.eclipse.ui.IViewPart;
-import org.eclipse.ui.IWorkbenchPage;
-import org.eclipse.ui.IWorkbenchPartReference;
-import org.eclipse.ui.IWorkbenchWindow;
-import org.eclipse.ui.PlatformUI;
-
-import com.redhat.thermostat.client.core.views.BasicView.Action;
-import com.redhat.thermostat.common.ActionNotifier;
-
-public class ViewVisibilityWatcher {
-    
-    private ActionNotifier<Action> notifier;
-    private boolean visible;
-
-    public ViewVisibilityWatcher(ActionNotifier<Action> notifier) {
-        this.notifier = notifier;
-        this.visible = false;
-    }
-    
-    public void watch(Composite parent, final String viewID) {
-        // Check if the view is currently visible
-        IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
-        IViewPart chartView = activePage.findView(viewID);
-        if (activePage.isPartVisible(chartView)) {
-            visible = true;
-            notifier.fireAction(Action.VISIBLE);
-        }
-        
-        // Register listener for visibility updates on the Eclipse view
-        final IPartListener2 partListener = new IPartListener2() {
-            
-            @Override
-            public void partVisible(IWorkbenchPartReference partRef) {
-                // The workbench fires a visible event when the view first takes
-                // focus, even if it was already on top
-                if (!visible && viewID.equals(partRef.getId())) {
-                    notifier.fireAction(Action.VISIBLE);
-                    visible = true;
-                }
-            }
-            
-            @Override
-            public void partHidden(IWorkbenchPartReference partRef) {
-                if (visible && viewID.equals(partRef.getId())) {
-                    notifier.fireAction(Action.HIDDEN);
-                    visible = false;
-                }
-            }
-
-            @Override
-            public void partOpened(IWorkbenchPartReference partRef) {
-            }
-            
-            @Override
-            public void partInputChanged(IWorkbenchPartReference partRef) {
-            }
-            
-            @Override
-            public void partDeactivated(IWorkbenchPartReference partRef) {
-            }
-            
-            @Override
-            public void partClosed(IWorkbenchPartReference partRef) {
-            }
-            
-            @Override
-            public void partBroughtToTop(IWorkbenchPartReference partRef) {
-            }
-            
-            @Override
-            public void partActivated(IWorkbenchPartReference partRef) {
-            }
-        };
-        
-        PlatformUI.getWorkbench().getActiveWorkbenchWindow().getPartService().addPartListener(partListener);
-        
-        parent.addDisposeListener(new DisposeListener() {
-            
-            @Override
-            public void widgetDisposed(DisposeEvent e) {
-                PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
-                    
-                    @Override
-                    public void run() {
-                        IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
-                        if (window != null) {
-                            window.getPartService().removePartListener(partListener);
-                        }
-                    }
-                });
-            }
-        });
-    }
-
-}
--- a/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/VmCpuViewPart.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/VmCpuViewPart.java	Mon Nov 19 11:40:08 2012 +0100
@@ -36,26 +36,29 @@
 
 package com.redhat.thermostat.eclipse.chart.common;
 
+import org.eclipse.swt.widgets.Composite;
+
 import com.redhat.thermostat.client.core.views.VmCpuViewProvider;
 import com.redhat.thermostat.client.ui.VmCpuController;
 import com.redhat.thermostat.common.dao.VmCpuStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.utils.OSGIUtils;
 import com.redhat.thermostat.eclipse.SWTComponent;
+import com.redhat.thermostat.eclipse.views.VmRefViewPart;
 
 public class VmCpuViewPart extends VmRefViewPart {
 
-    private VmCpuController controller;
-
     @Override
-    protected void createControllerView(VmRef ref) {
+    protected SWTComponent createControllerView(VmRef ref, Composite parent) {
         VmCpuStatDAO vmCpuStatDao = OSGIUtils.getInstance().getService(
                 VmCpuStatDAO.class);
-        VmCpuViewProvider viewProvider = OSGIUtils.getInstance().getService(
-                VmCpuViewProvider.class);
-        controller = createController(vmCpuStatDao, ref, viewProvider);
+        SWTVmCpuViewProvider viewProvider = (SWTVmCpuViewProvider) OSGIUtils
+                .getInstance().getService(VmCpuViewProvider.class);
+        viewProvider.setParent(parent);
+        VmCpuController controller = createController(vmCpuStatDao, ref,
+                viewProvider);
         SWTComponent view = (SWTComponent) controller.getView();
-        view.createControl(top);
+        return view;
     }
 
     public VmCpuController createController(VmCpuStatDAO vmCpuStatDao,
--- a/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/VmGcViewPart.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/VmGcViewPart.java	Mon Nov 19 11:40:08 2012 +0100
@@ -36,6 +36,8 @@
 
 package com.redhat.thermostat.eclipse.chart.common;
 
+import org.eclipse.swt.widgets.Composite;
+
 import com.redhat.thermostat.client.core.views.VmGcViewProvider;
 import com.redhat.thermostat.client.ui.VmGcController;
 import com.redhat.thermostat.common.dao.VmGcStatDAO;
@@ -43,23 +45,23 @@
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.utils.OSGIUtils;
 import com.redhat.thermostat.eclipse.SWTComponent;
+import com.redhat.thermostat.eclipse.views.VmRefViewPart;
 
 public class VmGcViewPart extends VmRefViewPart {
 
-    private VmGcController controller;
-
     @Override
-    protected void createControllerView(VmRef ref) {
+    protected SWTComponent createControllerView(VmRef ref, Composite parent) {
         VmMemoryStatDAO vmMemoryStatDao = OSGIUtils.getInstance().getService(
                 VmMemoryStatDAO.class);
         VmGcStatDAO vmGcStatDao = OSGIUtils.getInstance().getService(
                 VmGcStatDAO.class);
-        VmGcViewProvider viewProvider = OSGIUtils.getInstance().getService(
+        SWTVmGcViewProvider viewProvider = (SWTVmGcViewProvider) OSGIUtils.getInstance().getService(
                 VmGcViewProvider.class);
-        controller = createController(vmMemoryStatDao, vmGcStatDao, ref,
+        viewProvider.setParent(parent);
+        VmGcController controller = createController(vmMemoryStatDao, vmGcStatDao, ref,
                 viewProvider);
         SWTComponent view = (SWTComponent) controller.getView();
-        view.createControl(top);
+        return view;
     }
 
     public VmGcController createController(VmMemoryStatDAO vmMemoryStatDao,
--- a/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/VmRefViewPart.java	Mon Nov 19 11:35:17 2012 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <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.eclipse.chart.common;
-
-import com.redhat.thermostat.common.dao.VmRef;
-
-public abstract class VmRefViewPart extends RefViewPart<VmRef> {
-
-    public VmRefViewPart() {
-        super();
-    }
-
-    @Override
-    protected VmRef getRefFromSelection(Object selection) {
-        VmRef ref = null;
-        if (selection instanceof VmRef) {
-            ref = (VmRef) selection;
-        }
-        return ref;
-    }
-
-    @Override
-    protected String getNoSelectionMessage() {
-        return "No VM selected";
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.vmclassstat/.classpath	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.vmclassstat/.project	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>com.redhat.thermostat.eclipse.chart.vmclassstat</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.vmclassstat/META-INF/MANIFEST.MF	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,34 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Thermostat VM Classes View
+Bundle-SymbolicName: com.redhat.thermostat.eclipse.chart.vmclassstat;singleton:=true
+Bundle-Version: 0.5.0.qualifier
+Bundle-Activator: com.redhat.thermostat.eclipse.chart.vmclassstat.Activator
+Bundle-Vendor: Red Hat Inc.
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
+Bundle-ActivationPolicy: lazy
+Import-Package: com.redhat.thermostat.client.core.views,
+ com.redhat.thermostat.client.locale,
+ com.redhat.thermostat.client.osgi.service,
+ com.redhat.thermostat.client.ui,
+ com.redhat.thermostat.client.vmclassstat.core,
+ com.redhat.thermostat.common,
+ com.redhat.thermostat.common.appctx,
+ com.redhat.thermostat.common.dao,
+ com.redhat.thermostat.common.locale,
+ com.redhat.thermostat.common.utils,
+ com.redhat.thermostat.eclipse,
+ com.redhat.thermostat.eclipse.chart.common,
+ com.redhat.thermostat.eclipse.internal.views,
+ com.redhat.thermostat.eclipse.views,
+ com.redhat.thermostat.storage.model,
+ org.jfree.chart,
+ org.jfree.chart.axis,
+ org.jfree.chart.plot,
+ org.jfree.data,
+ org.jfree.data.general,
+ org.jfree.data.time,
+ org.jfree.data.xy
+Export-Package: com.redhat.thermostat.eclipse.chart.vmclassstat
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.vmclassstat/build.properties	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,5 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               plugin.xml
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.vmclassstat/plugin.xml	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+   <extension
+         point="org.eclipse.ui.views">
+      <view
+            class="com.redhat.thermostat.eclipse.chart.vmclassstat.VmClassStatViewPart"
+            id="com.redhat.thermostat.eclipse.chart.vmClassStatView"
+            name="VM Classes"
+            restorable="true">
+      </view>
+   </extension>
+   <extension
+         point="org.eclipse.ui.perspectiveExtensions">
+      <perspectiveExtension
+            targetID="com.redhat.thermostat.eclipse.perspective">
+         <view
+               id="com.redhat.thermostat.eclipse.chart.vmClassStatView"
+               minimized="false"
+               relationship="stack"
+               relative="com.redhat.thermostat.eclipse.chart.vmGcView">
+         </view>
+         <viewShortcut
+               id="com.redhat.thermostat.eclipse.chart.vmClassStatView">
+         </viewShortcut>
+      </perspectiveExtension>
+   </extension>
+
+</plugin>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.vmclassstat/pom.xml	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>thermostat-eclipse-parent</artifactId>
+    <groupId>com.redhat.thermostat.eclipse.parent</groupId>
+    <version>0.5.0-SNAPSHOT</version>
+  </parent>
+  <groupId>com.redhat.thermostat.eclipse.parent</groupId>
+  <artifactId>com.redhat.thermostat.eclipse.chart.vmclassstat</artifactId>
+  <packaging>eclipse-plugin</packaging>
+  <version>0.5.0-SNAPSHOT</version>
+
+  <name>Thermostat Eclipse VmClassStat Plug-in</name>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.redhat.thermostat.eclipse.parent</groupId>
+      <artifactId>com.redhat.thermostat.client.feature</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
+    </dependency>
+  </dependencies>
+
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.vmclassstat/src/com/redhat/thermostat/eclipse/chart/vmclassstat/Activator.java	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.eclipse.chart.vmclassstat;
+
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+import com.redhat.thermostat.client.vmclassstat.core.VmClassStatViewProvider;
+import com.redhat.thermostat.common.utils.OSGIUtils;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator extends AbstractUIPlugin {
+
+    // The plug-in ID
+    public static final String PLUGIN_ID = "com.redhat.thermostat.eclipse.chart.vmclassstat"; //$NON-NLS-1$
+
+    public static final String VIEW_ID_VM_CLASS_STAT = "com.redhat.thermostat.eclipse.chart.vmClassStatView";
+
+    // The shared instance
+    private static Activator plugin;
+
+    /**
+     * The constructor
+     */
+    public Activator() {
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext
+     * )
+     */
+    public void start(BundleContext context) throws Exception {
+        super.start(context);
+        plugin = this;
+
+        // Register view
+        OSGIUtils.getInstance().registerService(VmClassStatViewProvider.class,
+                new SWTVmClassStatViewProvider(), null);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext
+     * )
+     */
+    public void stop(BundleContext context) throws Exception {
+        plugin = null;
+        super.stop(context);
+    }
+
+    /**
+     * Returns the shared instance
+     * 
+     * @return the shared instance
+     */
+    public static Activator getDefault() {
+        return plugin;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.vmclassstat/src/com/redhat/thermostat/eclipse/chart/vmclassstat/SWTVmClassStatView.java	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.eclipse.chart.vmclassstat;
+
+import java.awt.EventQueue;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.axis.NumberTickUnit;
+import org.jfree.chart.axis.TickUnits;
+import org.jfree.data.RangeType;
+import org.jfree.data.time.FixedMillisecond;
+import org.jfree.data.time.RegularTimePeriod;
+import org.jfree.data.time.TimeSeries;
+import org.jfree.data.time.TimeSeriesCollection;
+
+import com.redhat.thermostat.client.vmclassstat.core.LocaleResources;
+import com.redhat.thermostat.client.vmclassstat.core.VmClassStatView;
+import com.redhat.thermostat.common.locale.Translate;
+import com.redhat.thermostat.eclipse.SWTComponent;
+import com.redhat.thermostat.eclipse.chart.common.RecentTimeSeriesChartComposite;
+import com.redhat.thermostat.storage.model.DiscreteTimeData;
+
+public class SWTVmClassStatView extends VmClassStatView implements SWTComponent {
+    
+    private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
+
+    private final TimeSeriesCollection dataset;
+    
+    private JFreeChart chart;
+
+    public SWTVmClassStatView(Composite parent) {
+        dataset = new TimeSeriesCollection();
+        // any name works
+        dataset.addSeries(new TimeSeries("class-stat"));
+        
+        chart = ChartFactory.createTimeSeriesChart(
+                null,
+                translator.localize(LocaleResources.VM_CLASSES_CHART_REAL_TIME_LABEL),
+                translator.localize(LocaleResources.VM_CLASSES_CHART_LOADED_CLASSES_LABEL),
+                dataset,
+                false, false, false);
+        
+        TickUnits tickUnits = new TickUnits();
+        tickUnits.add(new NumberTickUnit(1));
+        tickUnits.add(new NumberTickUnit(10));
+        tickUnits.add(new NumberTickUnit(100));
+        tickUnits.add(new NumberTickUnit(1000));
+        tickUnits.add(new NumberTickUnit(10000));
+        tickUnits.add(new NumberTickUnit(100000));
+        tickUnits.add(new NumberTickUnit(1000000));
+        
+        NumberAxis axis = (NumberAxis) chart.getXYPlot().getRangeAxis();
+        axis.setStandardTickUnits(tickUnits);
+        axis.setRangeType(RangeType.POSITIVE);
+        axis.setAutoRangeMinimumSize(10);
+        
+        Composite chartPanel = new RecentTimeSeriesChartComposite(parent, SWT.NONE, chart);
+        chartPanel.setLayout(new GridLayout());
+        chartPanel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+    }
+    
+    @Override
+    public void addClassCount(List<DiscreteTimeData<Long>> data) {
+        final List<DiscreteTimeData<Long>> copy = new ArrayList<>(data);
+        EventQueue.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                TimeSeries series = dataset.getSeries(0);
+                for (DiscreteTimeData<Long> data: copy) {
+                    RegularTimePeriod period = new FixedMillisecond(data.getTimeInMillis());
+                    if (series.getDataItem(period) == null) {
+                        series.add(period, data.getData(), false);
+                    }
+                }
+                series.fireSeriesChanged();
+            }
+        });
+    }
+
+    @Override
+    public void clearClassCount() {
+        EventQueue.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                TimeSeries series = dataset.getSeries(0);
+                series.clear();
+            }
+        });
+    }
+    
+    public JFreeChart getChart() {
+        return chart;
+    }
+
+    @Override
+    public void show() {
+        notifier.fireAction(Action.VISIBLE);
+    }
+
+    @Override
+    public void hide() {
+        notifier.fireAction(Action.HIDDEN);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.vmclassstat/src/com/redhat/thermostat/eclipse/chart/vmclassstat/SWTVmClassStatViewProvider.java	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.eclipse.chart.vmclassstat;
+
+import com.redhat.thermostat.client.vmclassstat.core.VmClassStatView;
+import com.redhat.thermostat.client.vmclassstat.core.VmClassStatViewProvider;
+import com.redhat.thermostat.eclipse.SWTViewProvider;
+
+public class SWTVmClassStatViewProvider extends SWTViewProvider implements
+        VmClassStatViewProvider {
+
+    @Override
+    public VmClassStatView createView() {
+        return new SWTVmClassStatView(getParent());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.vmclassstat/src/com/redhat/thermostat/eclipse/chart/vmclassstat/VmClassStatViewPart.java	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.eclipse.chart.vmclassstat;
+
+import org.eclipse.swt.widgets.Composite;
+
+import com.redhat.thermostat.client.vmclassstat.core.VmClassStatController;
+import com.redhat.thermostat.client.vmclassstat.core.VmClassStatViewProvider;
+import com.redhat.thermostat.common.dao.VmClassStatDAO;
+import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.common.utils.OSGIUtils;
+import com.redhat.thermostat.eclipse.SWTComponent;
+import com.redhat.thermostat.eclipse.views.VmRefViewPart;
+
+public class VmClassStatViewPart extends VmRefViewPart {
+
+    @Override
+    protected SWTComponent createControllerView(VmRef ref, Composite parent) {
+        SWTVmClassStatViewProvider viewProvider = (SWTVmClassStatViewProvider) OSGIUtils
+                .getInstance().getService(VmClassStatViewProvider.class);
+        viewProvider.setParent(parent);
+        VmClassStatDAO classStatDAO = OSGIUtils.getInstance().getService(
+                VmClassStatDAO.class);
+        VmClassStatController controller = createController(classStatDAO, ref,
+                viewProvider);
+        SWTComponent view = (SWTComponent) controller.getView();
+        return view;
+    }
+
+    public VmClassStatController createController(VmClassStatDAO classStatDao,
+            VmRef ref, VmClassStatViewProvider viewProvider) {
+        return new VmClassStatController(classStatDao, ref, viewProvider);
+    }
+
+}
--- a/eclipse/com.redhat.thermostat.eclipse.feature/feature.xml	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.feature/feature.xml	Mon Nov 19 11:40:08 2012 +0100
@@ -13,7 +13,7 @@
    </copyright>
 
    <license url="http://www.gnu.org/licenses/">
-Copyright 2012 Red Hat, Inc.
+      Copyright 2012 Red Hat, Inc.
 
 This file is part of Thermostat.
 
@@ -74,4 +74,11 @@
          version="0.0.0"
          unpack="false"/>
 
+   <plugin
+         id="com.redhat.thermostat.eclipse.chart.vmclassstat"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
 </feature>
--- a/eclipse/com.redhat.thermostat.eclipse.feature/pom.xml	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.feature/pom.xml	Mon Nov 19 11:40:08 2012 +0100
@@ -16,25 +16,9 @@
   <dependencies>
     <dependency>
       <groupId>com.redhat.thermostat.eclipse.parent</groupId>
-      <artifactId>com.redhat.thermostat.eclipse</artifactId>
-      <version>0.5.0-SNAPSHOT</version>
-    </dependency>
-    <!-- using Web storage -->
-    <dependency>
-      <groupId>com.redhat.thermostat</groupId>
-      <artifactId>thermostat-web-client</artifactId>
+      <artifactId>com.redhat.thermostat.client.feature</artifactId>
       <version>0.5.0-SNAPSHOT</version>
-    </dependency>
-    <dependency>
-      <groupId>com.redhat.thermostat</groupId>
-      <artifactId>thermostat-web-common</artifactId>
-      <version>0.5.0-SNAPSHOT</version>
-    </dependency>
-    <!-- DbService is in the launcher bundle -->
-    <dependency>
-      <groupId>com.redhat.thermostat</groupId>
-      <artifactId>thermostat-launcher</artifactId>
-      <version>0.5.0-SNAPSHOT</version>
+      <type>eclipse-feature</type>
     </dependency>
   </dependencies>
 
--- a/eclipse/com.redhat.thermostat.eclipse.p2-repo/pom.xml	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.p2-repo/pom.xml	Mon Nov 19 11:40:08 2012 +0100
@@ -16,25 +16,9 @@
   <dependencies>
     <dependency>
       <groupId>com.redhat.thermostat.eclipse.parent</groupId>
-      <artifactId>com.redhat.thermostat.eclipse</artifactId>
-      <version>0.5.0-SNAPSHOT</version>
-    </dependency>
-    <!-- using Web storage -->
-    <dependency>
-      <groupId>com.redhat.thermostat</groupId>
-      <artifactId>thermostat-web-client</artifactId>
+      <artifactId>com.redhat.thermostat.client.feature</artifactId>
       <version>0.5.0-SNAPSHOT</version>
-    </dependency>
-    <dependency>
-      <groupId>com.redhat.thermostat</groupId>
-      <artifactId>thermostat-web-common</artifactId>
-      <version>0.5.0-SNAPSHOT</version>
-    </dependency>
-    <!-- DbService is in the launcher bundle -->
-    <dependency>
-      <groupId>com.redhat.thermostat</groupId>
-      <artifactId>thermostat-launcher</artifactId>
-      <version>0.5.0-SNAPSHOT</version>
+      <type>eclipse-feature</type>
     </dependency>
   </dependencies>
 
--- a/eclipse/com.redhat.thermostat.eclipse.test.ui/META-INF/MANIFEST.MF	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.test.ui/META-INF/MANIFEST.MF	Mon Nov 19 11:40:08 2012 +0100
@@ -12,11 +12,14 @@
 Import-Package: com.redhat.thermostat.client.core.views,
  com.redhat.thermostat.client.osgi.service,
  com.redhat.thermostat.client.ui,
+ com.redhat.thermostat.client.vmclassstat.core,
  com.redhat.thermostat.common,
  com.redhat.thermostat.common.appctx,
  com.redhat.thermostat.storage.model,
  com.redhat.thermostat.eclipse,
  com.redhat.thermostat.eclipse.chart.common,
+ com.redhat.thermostat.eclipse.chart.vmclassstat,
+ com.redhat.thermostat.eclipse.internal.views,
  org.apache.log4j;version="1.2.13",
  org.eclipse.swtbot.eclipse.finder,
  org.eclipse.swtbot.eclipse.finder.matchers,
--- a/eclipse/com.redhat.thermostat.eclipse.test.ui/pom.xml	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.test.ui/pom.xml	Mon Nov 19 11:40:08 2012 +0100
@@ -20,21 +20,18 @@
       <layout>p2</layout>
       <url>http://download.eclipse.org/technology/swtbot/helios/dev-build/update-site</url>
     </repository>
+    <repository>
+      <id>local_client</id>
+      <layout>p2</layout>
+      <url>file://${basedir}/../core-p2-repository/target/repository/</url>
+    </repository>
+    <repository>
+      <id>local_eclipse</id>
+      <layout>p2</layout>
+      <url>file://${basedir}/../com.redhat.thermostat.eclipse.p2-repo/target/repository/</url>
+    </repository>
   </repositories>
 
-  <dependencies>
-    <dependency>
-      <groupId>com.redhat.thermostat.eclipse.parent</groupId>
-      <artifactId>com.redhat.thermostat.eclipse.chart.common</artifactId>
-      <version>0.5.0-SNAPSHOT</version>
-    </dependency>
-    <dependency>
-      <groupId>com.redhat.thermostat.eclipse.parent</groupId>
-      <artifactId>com.redhat.thermostat.eclipse.test-deps-repo</artifactId>
-      <version>0.5.0-SNAPSHOT</version>
-      <type>pom</type>
-    </dependency>
-  </dependencies>
   <build>
     <plugins>
       <plugin>
@@ -46,6 +43,23 @@
           <useUIThread>false</useUIThread>
           <product>org.eclipse.platform.ide</product>
           <application>org.eclipse.ui.ide.workbench</application>
+          <dependencies>
+            <dependency>
+              <type>p2-installable-unit</type>
+              <artifactId>org.eclipse.sdk.feature.group</artifactId>
+              <version>${sdk-version}</version>
+            </dependency>
+            <dependency>
+              <type>p2-installable-unit</type>
+              <artifactId>com.redhat.thermostat.client.feature.feature.group</artifactId>
+              <version>0.5.0</version>
+            </dependency>
+            <dependency>
+              <type>p2-installable-unit</type>
+              <artifactId>com.redhat.thermostat.eclipse.feature.feature.group</artifactId>
+              <version>0.5.0</version>
+            </dependency>
+          </dependencies>
           <bundleStartLevel>
             <bundle>
               <id>com.redhat.thermostat.common.core</id>
--- a/eclipse/com.redhat.thermostat.eclipse.test.ui/src/com/redhat/thermostat/eclipse/test/ui/SWTHostCpuViewTest.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.test.ui/src/com/redhat/thermostat/eclipse/test/ui/SWTHostCpuViewTest.java	Mon Nov 19 11:40:08 2012 +0100
@@ -40,6 +40,8 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.layout.GridData;
@@ -59,12 +61,17 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import com.redhat.thermostat.client.core.views.BasicView;
+import com.redhat.thermostat.client.core.views.BasicView.Action;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
 import com.redhat.thermostat.eclipse.ThermostatConstants;
 import com.redhat.thermostat.eclipse.chart.common.SWTHostCpuView;
 import com.redhat.thermostat.storage.model.DiscreteTimeData;
 
 @RunWith(SWTBotJunit4ClassRunner.class)
 public class SWTHostCpuViewTest {
+    private static final long TIMEOUT = 5000L;
     private SWTWorkbenchBot bot;
     private SWTHostCpuView view;
     private Shell shell;
@@ -82,8 +89,7 @@
                 parent.setLayout(new GridLayout());
                 parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,
                         true));
-                view = new SWTHostCpuView();
-                view.createControl(parent);
+                view = new SWTHostCpuView(parent);
                 shell.open();
             }
         });
@@ -331,5 +337,41 @@
             }
         });
     }
+    
+    @Test
+    public void testShowView() throws Exception {
+        final Action[] action = new Action[1];
+        final CountDownLatch latch = new CountDownLatch(1);
+        view.addActionListener(new ActionListener<BasicView.Action>() {
+            
+            @Override
+            public void actionPerformed(ActionEvent<Action> actionEvent) {
+                action[0] = actionEvent.getActionId();
+                latch.countDown();
+            }
+        });
+        
+        view.show();
+        latch.await(TIMEOUT, TimeUnit.MILLISECONDS);
+        assertEquals(Action.VISIBLE, action[0]);
+    }
+    
+    @Test
+    public void testHideView() throws Exception {
+        final Action[] action = new Action[1];
+        final CountDownLatch latch = new CountDownLatch(1);
+        view.addActionListener(new ActionListener<BasicView.Action>() {
+            
+            @Override
+            public void actionPerformed(ActionEvent<Action> actionEvent) {
+                action[0] = actionEvent.getActionId();
+                latch.countDown();
+            }
+        });
+        
+        view.hide();
+        latch.await(TIMEOUT, TimeUnit.MILLISECONDS);
+        assertEquals(Action.HIDDEN, action[0]);
+    }
 
 }
--- a/eclipse/com.redhat.thermostat.eclipse.test.ui/src/com/redhat/thermostat/eclipse/test/ui/SWTHostMemoryViewTest.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.test.ui/src/com/redhat/thermostat/eclipse/test/ui/SWTHostMemoryViewTest.java	Mon Nov 19 11:40:08 2012 +0100
@@ -40,6 +40,8 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.layout.GridData;
@@ -65,13 +67,18 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import com.redhat.thermostat.client.core.views.BasicView;
+import com.redhat.thermostat.client.core.views.BasicView.Action;
 import com.redhat.thermostat.client.core.views.HostMemoryView.GraphVisibilityChangeListener;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
 import com.redhat.thermostat.eclipse.ThermostatConstants;
 import com.redhat.thermostat.eclipse.chart.common.SWTHostMemoryView;
 import com.redhat.thermostat.storage.model.DiscreteTimeData;
 
 @RunWith(SWTBotJunit4ClassRunner.class)
 public class SWTHostMemoryViewTest implements GraphVisibilityChangeListener {
+    private static final long TIMEOUT = 5000L;
     private SWTWorkbenchBot bot;
     private SWTHostMemoryView view;
     private Shell shell;
@@ -89,8 +96,7 @@
                 parent.setLayout(new GridLayout());
                 parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,
                         true));
-                view = new SWTHostMemoryView();
-                view.createControl(parent);
+                view = new SWTHostMemoryView(parent);
                 view.addGraphVisibilityListener(SWTHostMemoryViewTest.this);
                 shell.open();
             }
@@ -538,4 +544,40 @@
     public void hide(String tag) {
         view.hideMemoryChart(tag);
     }
+    
+    @Test
+    public void testShowView() throws Exception {
+        final Action[] action = new Action[1];
+        final CountDownLatch latch = new CountDownLatch(1);
+        view.addActionListener(new ActionListener<BasicView.Action>() {
+            
+            @Override
+            public void actionPerformed(ActionEvent<Action> actionEvent) {
+                action[0] = actionEvent.getActionId();
+                latch.countDown();
+            }
+        });
+        
+        view.show();
+        latch.await(TIMEOUT, TimeUnit.MILLISECONDS);
+        assertEquals(Action.VISIBLE, action[0]);
+    }
+    
+    @Test
+    public void testHideView() throws Exception {
+        final Action[] action = new Action[1];
+        final CountDownLatch latch = new CountDownLatch(1);
+        view.addActionListener(new ActionListener<BasicView.Action>() {
+            
+            @Override
+            public void actionPerformed(ActionEvent<Action> actionEvent) {
+                action[0] = actionEvent.getActionId();
+                latch.countDown();
+            }
+        });
+        
+        view.hide();
+        latch.await(TIMEOUT, TimeUnit.MILLISECONDS);
+        assertEquals(Action.HIDDEN, action[0]);
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eclipse/com.redhat.thermostat.eclipse.test.ui/src/com/redhat/thermostat/eclipse/test/ui/SWTHostOverviewViewTest.java	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,408 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.eclipse.test.ui;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot;
+import org.eclipse.swtbot.swt.finder.waits.DefaultCondition;
+import org.eclipse.swtbot.swt.finder.widgets.SWTBotLabel;
+import org.eclipse.swtbot.swt.finder.widgets.SWTBotTable;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.client.core.views.BasicView;
+import com.redhat.thermostat.client.core.views.BasicView.Action;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.eclipse.ThermostatConstants;
+import com.redhat.thermostat.eclipse.internal.views.SWTHostOverviewView;
+
+public class SWTHostOverviewViewTest {
+    private static final long TIMEOUT = 5000L;
+    private SWTWorkbenchBot bot;
+    private SWTHostOverviewView view;
+    private Shell shell;
+
+    @Before
+    public void beforeTest() throws Exception {
+        bot = new SWTWorkbenchBot();
+
+        Display.getDefault().syncExec(new Runnable() {
+
+            @Override
+            public void run() {
+                shell = new Shell(Display.getCurrent());
+                Composite parent = new Composite(shell, SWT.NONE);
+                parent.setLayout(new GridLayout());
+                parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,
+                        true));
+                view = new SWTHostOverviewView(parent);
+                shell.open();
+            }
+        });
+    }
+
+    @After
+    public void afterTest() throws Exception {
+        Display.getDefault().syncExec(new Runnable() {
+
+            @Override
+            public void run() {
+                if (shell != null) {
+                    shell.close();
+                    view = null;
+                }
+            }
+        });
+    }
+
+    @Test
+    public void testSetHostName() {
+        final String hostname = "Test Host";
+
+        view.setHostName(hostname);
+
+        bot.waitUntil(new DefaultCondition() {
+
+            @Override
+            public boolean test() throws Exception {
+                SWTBotLabel label = bot.labelWithId(
+                        ThermostatConstants.TEST_TAG,
+                        SWTHostOverviewView.TEST_ID_HOSTNAME);
+                return label.getText().equals(hostname);
+            }
+
+            @Override
+            public String getFailureMessage() {
+                return "Hostname label not set";
+            }
+
+        });
+    }
+
+    @Test
+    public void testSetCpuModel() {
+        final String cpuModel = "Test CPU";
+
+        view.setCpuModel(cpuModel);
+
+        bot.waitUntil(new DefaultCondition() {
+
+            @Override
+            public boolean test() throws Exception {
+                SWTBotLabel label = bot.labelWithId(
+                        ThermostatConstants.TEST_TAG,
+                        SWTHostOverviewView.TEST_ID_PROC_MODEL);
+                return label.getText().equals(cpuModel);
+            }
+
+            @Override
+            public String getFailureMessage() {
+                return "CPU Model label not set";
+            }
+
+        });
+    }
+
+    @Test
+    public void testSetCpuCount() {
+        final String cpuCount = "8";
+
+        view.setCpuCount(cpuCount);
+
+        bot.waitUntil(new DefaultCondition() {
+
+            @Override
+            public boolean test() throws Exception {
+                SWTBotLabel label = bot.labelWithId(
+                        ThermostatConstants.TEST_TAG,
+                        SWTHostOverviewView.TEST_ID_PROC_CORE_COUNT);
+                return label.getText().equals(cpuCount);
+            }
+
+            @Override
+            public String getFailureMessage() {
+                return "CPU Count label not set";
+            }
+
+        });
+    }
+
+    @Test
+    public void testSetTotalMemory() {
+        final String totalMem = "100 TB";
+
+        view.setTotalMemory(totalMem);
+
+        bot.waitUntil(new DefaultCondition() {
+
+            @Override
+            public boolean test() throws Exception {
+                SWTBotLabel label = bot.labelWithId(
+                        ThermostatConstants.TEST_TAG,
+                        SWTHostOverviewView.TEST_ID_TOTAL_MEMORY);
+                return label.getText().equals(totalMem);
+            }
+
+            @Override
+            public String getFailureMessage() {
+                return "Total Memory label not set";
+            }
+
+        });
+    }
+
+    @Test
+    public void testSetOsName() {
+        final String osName = "Test OS";
+
+        view.setOsName(osName);
+
+        bot.waitUntil(new DefaultCondition() {
+
+            @Override
+            public boolean test() throws Exception {
+                SWTBotLabel label = bot.labelWithId(
+                        ThermostatConstants.TEST_TAG,
+                        SWTHostOverviewView.TEST_ID_OS_NAME);
+                return label.getText().equals(osName);
+            }
+
+            @Override
+            public String getFailureMessage() {
+                return "OS Name label not set";
+            }
+
+        });
+    }
+
+    @Test
+    public void testSetOsKernel() {
+        final String osKernel = "Test Kernel";
+
+        view.setOsKernel(osKernel);
+
+        bot.waitUntil(new DefaultCondition() {
+
+            @Override
+            public boolean test() throws Exception {
+                SWTBotLabel label = bot.labelWithId(
+                        ThermostatConstants.TEST_TAG,
+                        SWTHostOverviewView.TEST_ID_OS_KERNEL);
+                return label.getText().equals(osKernel);
+            }
+
+            @Override
+            public String getFailureMessage() {
+                return "OS Kernel label not set";
+            }
+
+        });
+    }
+
+    @Test
+    public void testSetNetworkTableColumns() {
+        final Object[] columns = { "Col 1", "Col 2", "Col 3" };
+        addColumns(columns);
+        
+        SWTBotTable table = bot.tableWithId(
+                ThermostatConstants.TEST_TAG,
+                SWTHostOverviewView.TEST_ID_NETWORK_INTERFACES);
+
+        List<String> tableColumns = table.columns();
+        assertEquals(columns[0], tableColumns.get(0));
+        assertEquals(columns[1], tableColumns.get(1));
+        assertEquals(columns[2], tableColumns.get(2));
+    }
+
+    protected void addColumns(final Object[] columns) {
+        view.setNetworkTableColumns(columns);
+
+        bot.waitUntil(new DefaultCondition() {
+
+            @Override
+            public boolean test() throws Exception {
+                SWTBotTable table = bot.tableWithId(
+                        ThermostatConstants.TEST_TAG,
+                        SWTHostOverviewView.TEST_ID_NETWORK_INTERFACES);
+
+                return table.columnCount() == 3;
+            }
+
+            @Override
+            public String getFailureMessage() {
+                return "Network Interfaces columns not set";
+            }
+
+        });
+    }
+
+    @Test
+    public void testSetInitialNetworkTableData() {
+        final Object[] columns = { "Col 1", "Col 2", "Col 3" };
+        final Object[][] data = { { "Iface 1", "IPv4 Addr 1", "IPv6 Addr 1" },
+                { "Iface 2", "IPv4 Addr 2", "IPv6 Addr 2" },
+                { "Iface 3", "IPv4 Addr 3", "IPv6 Addr 3" } };
+        
+        addColumns(columns);
+        addInitialData(data);
+        
+        SWTBotTable table = bot.tableWithId(
+                ThermostatConstants.TEST_TAG,
+                SWTHostOverviewView.TEST_ID_NETWORK_INTERFACES);
+        
+        assertEquals(data[0][0], table.cell(0, 0));
+        assertEquals(data[0][1], table.cell(0, 1));
+        assertEquals(data[0][2], table.cell(0, 2));
+        
+        assertEquals(data[1][0], table.cell(1, 0));
+        assertEquals(data[1][1], table.cell(1, 1));
+        assertEquals(data[1][2], table.cell(1, 2));
+        
+        assertEquals(data[2][0], table.cell(2, 0));
+        assertEquals(data[2][1], table.cell(2, 1));
+        assertEquals(data[2][2], table.cell(2, 2));
+    }
+
+    protected void addInitialData(final Object[][] data) {
+        view.setInitialNetworkTableData(data);
+        bot.waitUntil(new DefaultCondition() {
+
+            @Override
+            public boolean test() throws Exception {
+                SWTBotTable table = bot.tableWithId(
+                        ThermostatConstants.TEST_TAG,
+                        SWTHostOverviewView.TEST_ID_NETWORK_INTERFACES);
+
+                return table.rowCount() == 3;
+            }
+
+            @Override
+            public String getFailureMessage() {
+                return "Network Interfaces inital data not set";
+            }
+
+        });
+    }
+
+    @Test
+    public void testUpdateNetworkTableData() {
+        final Object[] columns = { "Col 1", "Col 2", "Col 3" };
+        final Object[][] data = { { "Iface 1", "IPv4 Addr 1", "IPv6 Addr 1" },
+                { "Iface 2", "IPv4 Addr 2", "IPv6 Addr 2" },
+                { "Iface 3", "IPv4 Addr 3", "IPv6 Addr 3" } };
+        
+        addColumns(columns);
+        addInitialData(data);
+        
+        changeItem(0, 0, "Change 1");
+        changeItem(0, 1, "Change 2");
+        changeItem(2, 1, "Change 3");
+        changeItem(1, 2, "Change 4");
+        changeItem(2, 2, "Change 5");
+    }
+
+    protected void changeItem(final int row, final int col, final String item) {
+        view.updateNetworkTableData(row, col, item);
+        bot.waitUntil(new DefaultCondition() {
+
+            @Override
+            public boolean test() throws Exception {
+                SWTBotTable table = bot.tableWithId(
+                        ThermostatConstants.TEST_TAG,
+                        SWTHostOverviewView.TEST_ID_NETWORK_INTERFACES);
+
+                return table.cell(row, col).equals(item);
+            }
+
+            @Override
+            public String getFailureMessage() {
+                return "Network Interfaces inital data not set";
+            }
+
+        });
+    }
+
+    @Test
+    public void testShowView() throws Exception {
+        final Action[] action = new Action[1];
+        final CountDownLatch latch = new CountDownLatch(1);
+        view.addActionListener(new ActionListener<BasicView.Action>() {
+
+            @Override
+            public void actionPerformed(ActionEvent<Action> actionEvent) {
+                action[0] = actionEvent.getActionId();
+                latch.countDown();
+            }
+        });
+
+        view.show();
+        latch.await(TIMEOUT, TimeUnit.MILLISECONDS);
+        assertEquals(Action.VISIBLE, action[0]);
+    }
+
+    @Test
+    public void testHideView() throws Exception {
+        final Action[] action = new Action[1];
+        final CountDownLatch latch = new CountDownLatch(1);
+        view.addActionListener(new ActionListener<BasicView.Action>() {
+
+            @Override
+            public void actionPerformed(ActionEvent<Action> actionEvent) {
+                action[0] = actionEvent.getActionId();
+                latch.countDown();
+            }
+        });
+
+        view.hide();
+        latch.await(TIMEOUT, TimeUnit.MILLISECONDS);
+        assertEquals(Action.HIDDEN, action[0]);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eclipse/com.redhat.thermostat.eclipse.test.ui/src/com/redhat/thermostat/eclipse/test/ui/SWTVmClassStatViewTest.java	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.eclipse.test.ui;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot;
+import org.eclipse.swtbot.swt.finder.waits.DefaultCondition;
+import org.jfree.chart.JFreeChart;
+import org.jfree.data.xy.XYDataset;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.client.core.views.BasicView;
+import com.redhat.thermostat.client.core.views.BasicView.Action;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.eclipse.chart.vmclassstat.SWTVmClassStatView;
+import com.redhat.thermostat.storage.model.DiscreteTimeData;
+
+public class SWTVmClassStatViewTest {
+    private static final long TIMEOUT = 5000L;
+    private SWTWorkbenchBot bot;
+    private SWTVmClassStatView view;
+    private Shell shell;
+
+    @Before
+    public void beforeTest() throws Exception {
+        bot = new SWTWorkbenchBot();
+        
+        Display.getDefault().syncExec(new Runnable() {
+
+            @Override
+            public void run() {
+                shell = new Shell(Display.getCurrent());
+                Composite parent = new Composite(shell, SWT.NONE);
+                parent.setLayout(new GridLayout());
+                parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,
+                        true));
+                view = new SWTVmClassStatView(parent);
+                shell.open();
+            }
+        });
+    }
+
+    @After
+    public void afterTest() throws Exception {
+        Display.getDefault().syncExec(new Runnable() {
+
+            @Override
+            public void run() {
+                if (shell != null) {
+                    shell.close();
+                    view = null;
+                }
+            }
+        });
+    }
+
+    @Test
+    public void testClearClassCount() {
+        List<DiscreteTimeData<Long>> data = new ArrayList<DiscreteTimeData<Long>>();
+        data.add(new DiscreteTimeData<Long>(1000L, 0L));
+        data.add(new DiscreteTimeData<Long>(2000L, 7L));
+        data.add(new DiscreteTimeData<Long>(3000L, 50L));
+        
+        addData(data);
+        
+        view.clearClassCount();
+        
+        // Wait until data removed
+        bot.waitUntil(new DefaultCondition() {
+            
+            @Override
+            public boolean test() throws Exception {
+                JFreeChart chart = view.getChart();
+                return chart.getXYPlot().getDataset().getItemCount(0) == 0;
+            }
+            
+            @Override
+            public String getFailureMessage() {
+                return "Data not cleared";
+            }
+        });
+    }
+
+    @Test
+    public void testAddClassCount() {
+        List<DiscreteTimeData<Long>> data = new ArrayList<DiscreteTimeData<Long>>();
+        data.add(new DiscreteTimeData<Long>(1000L, 0L));
+        data.add(new DiscreteTimeData<Long>(2000L, 7L));
+        data.add(new DiscreteTimeData<Long>(3000L, 50L));
+        
+        addData(data);
+        
+        // Verify data
+        XYDataset dataset = view.getChart().getXYPlot().getDataset();
+        assertEquals(1000L, dataset.getX(0, 0));
+        assertEquals(2000L, dataset.getX(0, 1));
+        assertEquals(3000L, dataset.getX(0, 2));
+        
+        assertEquals(0L, dataset.getY(0, 0));
+        assertEquals(7L, dataset.getY(0, 1));
+        assertEquals(50L, dataset.getY(0, 2));
+    }
+
+    public void addData(final List<DiscreteTimeData<Long>> data) {
+        view.addClassCount(data);
+        
+        bot.waitUntil(new DefaultCondition() {
+            
+            @Override
+            public boolean test() throws Exception {
+                JFreeChart chart = view.getChart();
+                return chart.getXYPlot().getDataset().getItemCount(0) == data.size();
+            }
+            
+            @Override
+            public String getFailureMessage() {
+                return "Data not added";
+            }
+        });
+    }
+    
+    @Test
+    public void testShowView() throws Exception {
+        final Action[] action = new Action[1];
+        final CountDownLatch latch = new CountDownLatch(1);
+        view.addActionListener(new ActionListener<BasicView.Action>() {
+            
+            @Override
+            public void actionPerformed(ActionEvent<Action> actionEvent) {
+                action[0] = actionEvent.getActionId();
+                latch.countDown();
+            }
+        });
+        
+        view.show();
+        latch.await(TIMEOUT, TimeUnit.MILLISECONDS);
+        assertEquals(Action.VISIBLE, action[0]);
+    }
+    
+    @Test
+    public void testHideView() throws Exception {
+        final Action[] action = new Action[1];
+        final CountDownLatch latch = new CountDownLatch(1);
+        view.addActionListener(new ActionListener<BasicView.Action>() {
+            
+            @Override
+            public void actionPerformed(ActionEvent<Action> actionEvent) {
+                action[0] = actionEvent.getActionId();
+                latch.countDown();
+            }
+        });
+        
+        view.hide();
+        latch.await(TIMEOUT, TimeUnit.MILLISECONDS);
+        assertEquals(Action.HIDDEN, action[0]);
+    }
+
+}
--- a/eclipse/com.redhat.thermostat.eclipse.test.ui/src/com/redhat/thermostat/eclipse/test/ui/SWTVmCpuViewTest.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.test.ui/src/com/redhat/thermostat/eclipse/test/ui/SWTVmCpuViewTest.java	Mon Nov 19 11:40:08 2012 +0100
@@ -40,6 +40,8 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.layout.GridData;
@@ -55,10 +57,15 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import com.redhat.thermostat.client.core.views.BasicView;
+import com.redhat.thermostat.client.core.views.BasicView.Action;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
 import com.redhat.thermostat.eclipse.chart.common.SWTVmCpuView;
 import com.redhat.thermostat.storage.model.DiscreteTimeData;
 
 public class SWTVmCpuViewTest {
+    private static final long TIMEOUT = 5000L;
     private SWTWorkbenchBot bot;
     private SWTVmCpuView view;
     private Shell shell;
@@ -76,8 +83,7 @@
                 parent.setLayout(new GridLayout());
                 parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,
                         true));
-                view = new SWTVmCpuView();
-                view.createControl(parent);
+                view = new SWTVmCpuView(parent);
                 shell.open();
             }
         });
@@ -162,5 +168,41 @@
             }
         });
     }
+    
+    @Test
+    public void testShowView() throws Exception {
+        final Action[] action = new Action[1];
+        final CountDownLatch latch = new CountDownLatch(1);
+        view.addActionListener(new ActionListener<BasicView.Action>() {
+            
+            @Override
+            public void actionPerformed(ActionEvent<Action> actionEvent) {
+                action[0] = actionEvent.getActionId();
+                latch.countDown();
+            }
+        });
+        
+        view.show();
+        latch.await(TIMEOUT, TimeUnit.MILLISECONDS);
+        assertEquals(Action.VISIBLE, action[0]);
+    }
+    
+    @Test
+    public void testHideView() throws Exception {
+        final Action[] action = new Action[1];
+        final CountDownLatch latch = new CountDownLatch(1);
+        view.addActionListener(new ActionListener<BasicView.Action>() {
+            
+            @Override
+            public void actionPerformed(ActionEvent<Action> actionEvent) {
+                action[0] = actionEvent.getActionId();
+                latch.countDown();
+            }
+        });
+        
+        view.hide();
+        latch.await(TIMEOUT, TimeUnit.MILLISECONDS);
+        assertEquals(Action.HIDDEN, action[0]);
+    }
 
 }
--- a/eclipse/com.redhat.thermostat.eclipse.test.ui/src/com/redhat/thermostat/eclipse/test/ui/SWTVmGcViewTest.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.test.ui/src/com/redhat/thermostat/eclipse/test/ui/SWTVmGcViewTest.java	Mon Nov 19 11:40:08 2012 +0100
@@ -41,6 +41,8 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.layout.GridData;
@@ -56,10 +58,15 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import com.redhat.thermostat.client.core.views.BasicView;
+import com.redhat.thermostat.client.core.views.BasicView.Action;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
 import com.redhat.thermostat.eclipse.chart.common.SWTVmGcView;
 import com.redhat.thermostat.storage.model.IntervalTimeData;
 
 public class SWTVmGcViewTest {
+    private static final long TIMEOUT = 5000L;
     private SWTWorkbenchBot bot;
     private SWTVmGcView view;
     private Shell shell;
@@ -77,8 +84,7 @@
                 parent.setLayout(new GridLayout());
                 parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,
                         true));
-                view = new SWTVmGcView();
-                view.createControl(parent);
+                view = new SWTVmGcView(parent);
                 shell.open();
             }
         });
@@ -296,5 +302,41 @@
         // Ensure other chart unchanged
         assertEquals(3, view.getChart(tag2).getXYPlot().getDataset().getItemCount(0));
     }
+    
+    @Test
+    public void testShowView() throws Exception {
+        final Action[] action = new Action[1];
+        final CountDownLatch latch = new CountDownLatch(1);
+        view.addActionListener(new ActionListener<BasicView.Action>() {
+            
+            @Override
+            public void actionPerformed(ActionEvent<Action> actionEvent) {
+                action[0] = actionEvent.getActionId();
+                latch.countDown();
+            }
+        });
+        
+        view.show();
+        latch.await(TIMEOUT, TimeUnit.MILLISECONDS);
+        assertEquals(Action.VISIBLE, action[0]);
+    }
+    
+    @Test
+    public void testHideView() throws Exception {
+        final Action[] action = new Action[1];
+        final CountDownLatch latch = new CountDownLatch(1);
+        view.addActionListener(new ActionListener<BasicView.Action>() {
+            
+            @Override
+            public void actionPerformed(ActionEvent<Action> actionEvent) {
+                action[0] = actionEvent.getActionId();
+                latch.countDown();
+            }
+        });
+        
+        view.hide();
+        latch.await(TIMEOUT, TimeUnit.MILLISECONDS);
+        assertEquals(Action.HIDDEN, action[0]);
+    }
 
 }
--- a/eclipse/com.redhat.thermostat.eclipse.test/META-INF/MANIFEST.MF	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.test/META-INF/MANIFEST.MF	Mon Nov 19 11:40:08 2012 +0100
@@ -15,13 +15,16 @@
 Import-Package: com.redhat.thermostat.client.core.views,
  com.redhat.thermostat.client.osgi.service,
  com.redhat.thermostat.client.ui,
+ com.redhat.thermostat.client.vmclassstat.core,
  com.redhat.thermostat.common,
  com.redhat.thermostat.common.dao,
  com.redhat.thermostat.common.utils,
  com.redhat.thermostat.eclipse,
  com.redhat.thermostat.eclipse.chart.common,
+ com.redhat.thermostat.eclipse.chart.vmclassstat,
  com.redhat.thermostat.eclipse.internal.model,
  com.redhat.thermostat.eclipse.internal.views,
+ com.redhat.thermostat.eclipse.views,
  org.mockito,
  org.mockito.stubbing
 Export-Package: com.redhat.thermostat.eclipse.test.model,
--- a/eclipse/com.redhat.thermostat.eclipse.test/pom.xml	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.test/pom.xml	Mon Nov 19 11:40:08 2012 +0100
@@ -14,36 +14,19 @@
 
   <name>Thermostat Eclipse Client Tests</name>
 
-  <dependencies>
-    <dependency>
-      <groupId>com.redhat.thermostat.eclipse.parent</groupId>
-      <artifactId>com.redhat.thermostat.eclipse</artifactId>
-      <version>0.5.0-SNAPSHOT</version>
-    </dependency>
-    <!-- using Web storage -->
-    <dependency>
-      <groupId>com.redhat.thermostat</groupId>
-      <artifactId>thermostat-web-client</artifactId>
-      <version>0.5.0-SNAPSHOT</version>
-    </dependency>
-    <dependency>
-      <groupId>com.redhat.thermostat</groupId>
-      <artifactId>thermostat-web-common</artifactId>
-      <version>0.5.0-SNAPSHOT</version>
-    </dependency>
-    <!-- DbService is in the launcher bundle -->
-    <dependency>
-      <groupId>com.redhat.thermostat</groupId>
-      <artifactId>thermostat-launcher</artifactId>
-      <version>0.5.0-SNAPSHOT</version>
-    </dependency>
-    <dependency>
-      <groupId>com.redhat.thermostat.eclipse.parent</groupId>
-      <artifactId>com.redhat.thermostat.eclipse.test-deps-repo</artifactId>
-      <version>0.5.0-SNAPSHOT</version>
-      <type>pom</type>
-    </dependency>
-  </dependencies>
+  <repositories>
+    <repository>
+      <id>local_client</id>
+      <layout>p2</layout>
+      <url>file://${basedir}/../core-p2-repository/target/repository/</url>
+    </repository>
+    <repository>
+      <id>local_eclipse</id>
+      <layout>p2</layout>
+      <url>file://${basedir}/../com.redhat.thermostat.eclipse.p2-repo/target/repository/</url>
+    </repository>
+  </repositories>
+
   <build>
     <plugins>
       <plugin>
@@ -60,6 +43,16 @@
               <artifactId>org.eclipse.sdk.feature.group</artifactId>
               <version>${sdk-version}</version>
             </dependency>
+            <dependency>
+              <type>p2-installable-unit</type>
+              <artifactId>com.redhat.thermostat.client.feature.feature.group</artifactId>
+              <version>0.5.0</version>
+            </dependency>
+            <dependency>
+              <type>p2-installable-unit</type>
+              <artifactId>com.redhat.thermostat.eclipse.feature.feature.group</artifactId>
+              <version>0.5.0</version>
+            </dependency>
           </dependencies>
           <bundleStartLevel>
             <bundle>
--- a/eclipse/com.redhat.thermostat.eclipse.test/src/com/redhat/thermostat/eclipse/test/views/AbstractRefViewPartTest.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.test/src/com/redhat/thermostat/eclipse/test/views/AbstractRefViewPartTest.java	Mon Nov 19 11:40:08 2012 +0100
@@ -55,8 +55,8 @@
 import com.redhat.thermostat.common.utils.OSGIUtils;
 import com.redhat.thermostat.eclipse.SWTComponent;
 import com.redhat.thermostat.eclipse.ThermostatConstants;
-import com.redhat.thermostat.eclipse.chart.common.RefViewPart;
 import com.redhat.thermostat.eclipse.internal.views.HostsVmsTreeViewPart;
+import com.redhat.thermostat.eclipse.internal.views.RefViewPart;
 
 public abstract class AbstractRefViewPartTest<T extends Ref> {
 
@@ -84,27 +84,32 @@
         IWorkbenchWindow window = mock(IWorkbenchWindow.class);
         IWorkbenchPage page = mock(IWorkbenchPage.class);
         ISelectionService service = mock(ISelectionService.class);
+        IWorkbenchPartSite site = mock(IWorkbenchPartSite.class);
         
         when(page.findView(ThermostatConstants.VIEW_ID_HOST_VM)).thenReturn(hostVMView);
         when(window.getSelectionService()).thenReturn(service);
         when(window.getActivePage()).thenReturn(page);
         when(view.getWorkbenchWindow()).thenReturn(window);
+        when(site.getId()).thenReturn(getViewID());
+        when(view.getSite()).thenReturn(site);
         
         // Controller mock
         mockController();
         
         // Selection mocks
-        IWorkbenchPartSite site = mock(IWorkbenchPartSite.class);
+        IWorkbenchPartSite hostVMSite = mock(IWorkbenchPartSite.class);
         provider = mock(ISelectionProvider.class);
  
-        when(site.getId()).thenReturn(ThermostatConstants.VIEW_ID_HOST_VM);
-        when(site.getSelectionProvider()).thenReturn(provider);
-        when(hostVMView.getSite()).thenReturn(site);
+        when(hostVMSite.getId()).thenReturn(ThermostatConstants.VIEW_ID_HOST_VM);
+        when(hostVMSite.getSelectionProvider()).thenReturn(provider);
+        when(hostVMView.getSite()).thenReturn(hostVMSite);
     }
 
     protected abstract void mockController();
 
     protected abstract RefViewPart<T> createViewPart();
+    
+    protected abstract String getViewID();
 
     protected IStructuredSelection mockSelection(Ref ref) {
         IStructuredSelection selection = mock(IStructuredSelection.class);;
--- a/eclipse/com.redhat.thermostat.eclipse.test/src/com/redhat/thermostat/eclipse/test/views/HostCpuViewPartTest.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.test/src/com/redhat/thermostat/eclipse/test/views/HostCpuViewPartTest.java	Mon Nov 19 11:40:08 2012 +0100
@@ -39,6 +39,7 @@
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.same;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -47,6 +48,7 @@
 import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.swt.widgets.Composite;
 import org.junit.Test;
+import org.mockito.InOrder;
 
 import com.redhat.thermostat.client.core.views.BasicView;
 import com.redhat.thermostat.client.core.views.HostCpuViewProvider;
@@ -55,11 +57,16 @@
 import com.redhat.thermostat.common.dao.HostInfoDAO;
 import com.redhat.thermostat.common.dao.HostRef;
 import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.eclipse.ThermostatConstants;
 import com.redhat.thermostat.eclipse.chart.common.HostCpuViewPart;
-import com.redhat.thermostat.eclipse.chart.common.RefViewPart;
 import com.redhat.thermostat.eclipse.chart.common.SWTHostCpuView;
+import com.redhat.thermostat.eclipse.chart.common.SWTHostCpuViewProvider;
+import com.redhat.thermostat.eclipse.internal.views.RefViewPart;
 
 public class HostCpuViewPartTest extends AbstractRefViewPartTest<HostRef> {
+    
+    private SWTHostCpuViewProvider viewProvider;
+    private HostCpuController controller;
 
     @Test
     public void testSetFocus() throws Exception {
@@ -76,6 +83,13 @@
     }
 
     @Test
+    public void testBadSelection() throws Exception {
+        mockSelection(null);
+        view.createPartControl(parent);
+        verify(view).createNoSelectionLabel();
+    }
+
+    @Test
     public void testSelectionBefore() throws Exception {
         HostRef hostRef = new HostRef("TEST", "Test");
         mockSelection(hostRef);
@@ -83,7 +97,13 @@
 
         verify(view, never()).createNoSelectionLabel();
 
-        verify(thermoView).createControl(any(Composite.class));
+        verifyViewProvider();
+    }
+
+    private void verifyViewProvider() {
+        InOrder order = inOrder(viewProvider, controller);
+        order.verify(viewProvider).setParent(any(Composite.class));
+        order.verify(controller).getView();
     }
 
     @Test
@@ -94,7 +114,7 @@
         IStructuredSelection selection = mockSelection(hostRef);
         view.selectionChanged(hostVMView, selection);
 
-        verify(thermoView).createControl(any(Composite.class));
+        verifyViewProvider();
     }
 
     @Test
@@ -106,7 +126,7 @@
         IStructuredSelection selection = mockSelection(vmRef);
         view.selectionChanged(hostVMView, selection);
 
-        verify(thermoView).createControl(any(Composite.class));
+        verifyViewProvider();
     }
 
     @Override
@@ -116,12 +136,12 @@
 
     @Override
     protected void mockController() {
-        HostCpuController controller = mock(HostCpuController.class);
+        controller = mock(HostCpuController.class);
         thermoView = mock(SWTHostCpuView.class);
 
         HostInfoDAO hostInfoDao = mock(HostInfoDAO.class);
         CpuStatDAO cpuStatDao = mock(CpuStatDAO.class);
-        HostCpuViewProvider viewProvider = mock(HostCpuViewProvider.class);
+        viewProvider = mock(SWTHostCpuViewProvider.class);
         when(osgi.getService(HostInfoDAO.class)).thenReturn(hostInfoDao);
         when(osgi.getService(CpuStatDAO.class)).thenReturn(cpuStatDao);
         when(osgi.getService(HostCpuViewProvider.class)).thenReturn(
@@ -133,4 +153,9 @@
         when(controller.getView()).thenReturn((BasicView) thermoView);
     }
 
+    @Override
+    protected String getViewID() {
+        return ThermostatConstants.VIEW_ID_HOST_CPU;
+    }
+
 }
--- a/eclipse/com.redhat.thermostat.eclipse.test/src/com/redhat/thermostat/eclipse/test/views/HostMemoryViewPartTest.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.test/src/com/redhat/thermostat/eclipse/test/views/HostMemoryViewPartTest.java	Mon Nov 19 11:40:08 2012 +0100
@@ -39,13 +39,14 @@
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.same;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.swt.widgets.Composite;
 import org.junit.Test;
+import org.mockito.InOrder;
 
 import com.redhat.thermostat.client.core.views.BasicView;
 import com.redhat.thermostat.client.core.views.HostMemoryViewProvider;
@@ -53,12 +54,17 @@
 import com.redhat.thermostat.common.dao.HostInfoDAO;
 import com.redhat.thermostat.common.dao.HostRef;
 import com.redhat.thermostat.common.dao.MemoryStatDAO;
+import com.redhat.thermostat.eclipse.ThermostatConstants;
 import com.redhat.thermostat.eclipse.chart.common.HostMemoryViewPart;
-import com.redhat.thermostat.eclipse.chart.common.RefViewPart;
 import com.redhat.thermostat.eclipse.chart.common.SWTHostMemoryView;
+import com.redhat.thermostat.eclipse.chart.common.SWTHostMemoryViewProvider;
+import com.redhat.thermostat.eclipse.internal.views.RefViewPart;
 
 public class HostMemoryViewPartTest extends AbstractRefViewPartTest<HostRef> {
 
+    private SWTHostMemoryViewProvider viewProvider;
+    private HostMemoryController controller;
+
     @Test
     public void testSelectionAfter() throws Exception {
         view.createPartControl(parent);
@@ -67,17 +73,23 @@
         IStructuredSelection selection = mockSelection(hostRef);
         view.selectionChanged(hostVMView, selection);
 
-        verify(thermoView).createControl(any(Composite.class));
+        verifyViewProvider();
+    }
+
+    private void verifyViewProvider() {
+        InOrder order = inOrder(viewProvider, controller);
+        order.verify(viewProvider).setParent(any(Composite.class));
+        order.verify(controller).getView();
     }
 
     @Override
     protected void mockController() {
-        HostMemoryController controller = mock(HostMemoryController.class);
+        controller = mock(HostMemoryController.class);
         thermoView = mock(SWTHostMemoryView.class);
 
         HostInfoDAO hostInfoDao = mock(HostInfoDAO.class);
         MemoryStatDAO memStatDao = mock(MemoryStatDAO.class);
-        HostMemoryViewProvider viewProvider = mock(HostMemoryViewProvider.class);
+        viewProvider = mock(SWTHostMemoryViewProvider.class);
         when(osgi.getService(HostInfoDAO.class)).thenReturn(hostInfoDao);
         when(osgi.getService(MemoryStatDAO.class)).thenReturn(memStatDao);
         when(osgi.getService(HostMemoryViewProvider.class)).thenReturn(
@@ -94,4 +106,9 @@
         return new HostMemoryViewPart();
     }
 
+    @Override
+    protected String getViewID() {
+        return ThermostatConstants.VIEW_ID_HOST_MEMORY;
+    }
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eclipse/com.redhat.thermostat.eclipse.test/src/com/redhat/thermostat/eclipse/test/views/HostOverviewViewPartTest.java	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.eclipse.test.views;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.widgets.Composite;
+import org.junit.Test;
+import org.mockito.InOrder;
+
+import com.redhat.thermostat.client.core.views.BasicView;
+import com.redhat.thermostat.client.core.views.HostOverviewViewProvider;
+import com.redhat.thermostat.client.ui.HostOverviewController;
+import com.redhat.thermostat.common.dao.HostInfoDAO;
+import com.redhat.thermostat.common.dao.HostRef;
+import com.redhat.thermostat.common.dao.NetworkInterfaceInfoDAO;
+import com.redhat.thermostat.eclipse.ThermostatConstants;
+import com.redhat.thermostat.eclipse.internal.views.HostOverviewViewPart;
+import com.redhat.thermostat.eclipse.internal.views.RefViewPart;
+import com.redhat.thermostat.eclipse.internal.views.SWTHostOverviewView;
+import com.redhat.thermostat.eclipse.internal.views.SWTHostOverviewViewProvider;
+
+public class HostOverviewViewPartTest extends AbstractRefViewPartTest<HostRef> {
+
+    private HostOverviewController controller;
+    private SWTHostOverviewViewProvider viewProvider;
+    
+    @Test
+    public void testSelectionAfter() throws Exception {
+        view.createPartControl(parent);
+
+        HostRef hostRef = new HostRef("TEST", "Test");
+        IStructuredSelection selection = mockSelection(hostRef);
+        view.selectionChanged(hostVMView, selection);
+
+        verifyViewProvider();
+    }
+
+    private void verifyViewProvider() {
+        InOrder order = inOrder(viewProvider, controller);
+        order.verify(viewProvider).setParent(any(Composite.class));
+        order.verify(controller).getView();
+    }
+
+    @Override
+    protected void mockController() {
+        controller = mock(HostOverviewController.class);
+        thermoView = mock(SWTHostOverviewView.class);
+
+        HostInfoDAO hostInfoDao = mock(HostInfoDAO.class);
+        NetworkInterfaceInfoDAO netIfaceDao = mock(NetworkInterfaceInfoDAO.class);
+        viewProvider = mock(SWTHostOverviewViewProvider.class);
+        when(osgi.getService(HostInfoDAO.class)).thenReturn(hostInfoDao);
+        when(osgi.getService(NetworkInterfaceInfoDAO.class)).thenReturn(
+                netIfaceDao);
+        when(osgi.getService(HostOverviewViewProvider.class)).thenReturn(
+                viewProvider);
+
+        doReturn(controller).when(((HostOverviewViewPart) view))
+                .createController(any(HostRef.class), same(hostInfoDao),
+                        same(netIfaceDao), same(viewProvider));
+        when(controller.getView()).thenReturn((BasicView) thermoView);
+    }
+
+    @Override
+    protected RefViewPart<HostRef> createViewPart() {
+        return new HostOverviewViewPart();
+    }
+
+    @Override
+    protected String getViewID() {
+        return ThermostatConstants.VIEW_ID_HOST_OVERVIEW;
+    }
+
+}
--- a/eclipse/com.redhat.thermostat.eclipse.test/src/com/redhat/thermostat/eclipse/test/views/ViewVisibilityWatcherTest.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.test/src/com/redhat/thermostat/eclipse/test/views/ViewVisibilityWatcherTest.java	Mon Nov 19 11:40:08 2012 +0100
@@ -52,12 +52,9 @@
 import org.junit.Before;
 import org.junit.Test;
 
-import com.redhat.thermostat.client.core.views.BasicView;
 import com.redhat.thermostat.client.core.views.BasicView.Action;
-import com.redhat.thermostat.common.ActionEvent;
-import com.redhat.thermostat.common.ActionListener;
-import com.redhat.thermostat.common.ActionNotifier;
-import com.redhat.thermostat.eclipse.chart.common.ViewVisibilityWatcher;
+import com.redhat.thermostat.eclipse.SWTComponent;
+import com.redhat.thermostat.eclipse.internal.views.ViewVisibilityWatcher;
 
 public class ViewVisibilityWatcherTest {
     private static final Long TIME_OUT_MILLIS = 5000L;
@@ -65,10 +62,32 @@
     private IViewPart view;
     private Shell shell;
     private CountDownLatch latch;
+    private Action action;
+    private ViewVisibilityWatcher watcher;
 
     @Before
     public void beforeTest() throws Exception {
         shell = new Shell(Display.getCurrent());
+        
+        latch = new CountDownLatch(1);
+        action = null;
+        
+        SWTComponent component = new SWTComponent() {
+            
+            @Override
+            public void show() {
+                action = Action.VISIBLE;
+                latch.countDown();
+            }
+            
+            @Override
+            public void hide() {
+                action = Action.HIDDEN;
+                latch.countDown();
+            }
+        };
+        
+        watcher = new ViewVisibilityWatcher(component);
     }
 
     @After
@@ -78,96 +97,51 @@
 
     @Test
     public void testVisibleBeforeAttach() throws Exception {
-        final CountDownLatch latch = new CountDownLatch(1);
-        final Action[] action = new Action[1];
-
-        ActionNotifier<Action> notifier = new ActionNotifier<>(this);
-        notifier.addActionListener(new ActionListener<BasicView.Action>() {
-
-            @Override
-            public void actionPerformed(ActionEvent<Action> actionEvent) {
-                action[0] = actionEvent.getActionId();
-                latch.countDown();
-            }
-        });
-
-        ViewVisibilityWatcher watcher = new ViewVisibilityWatcher(notifier);
-
         showView();
 
         // Attach
         watcher.watch(shell, VIEW_ID);
 
-        waitForAction(latch);
+        waitForAction();
 
-        assertEquals(Action.VISIBLE, action[0]);
+        assertEquals(Action.VISIBLE, action);
     }
 
     @Test
     public void testVisibleAfterAttach() throws Exception {
-        final CountDownLatch latch = new CountDownLatch(1);
-        final Action[] action = new Action[1];
-
-        ActionNotifier<Action> notifier = new ActionNotifier<>(this);
-        notifier.addActionListener(new ActionListener<BasicView.Action>() {
-
-            @Override
-            public void actionPerformed(ActionEvent<Action> actionEvent) {
-                action[0] = actionEvent.getActionId();
-                latch.countDown();
-            }
-        });
-
-        ViewVisibilityWatcher watcher = new ViewVisibilityWatcher(notifier);
-
         // Attach
         watcher.watch(shell, VIEW_ID);
 
         showView();
 
-        waitForAction(latch);
+        waitForAction();
 
-        assertEquals(Action.VISIBLE, action[0]);
+        assertEquals(Action.VISIBLE, action);
     }
 
     @Test
     public void testVisibleBeforeAttachHiddenAfter() throws Exception {
-        latch = new CountDownLatch(1);
-
-        final Action[] action = new Action[1];
-
-        ActionNotifier<Action> notifier = new ActionNotifier<>(this);
-        notifier.addActionListener(new ActionListener<BasicView.Action>() {
-
-            @Override
-            public void actionPerformed(ActionEvent<Action> actionEvent) {
-                action[0] = actionEvent.getActionId();
-                latch.countDown();
-            }
-        });
-
-        ViewVisibilityWatcher watcher = new ViewVisibilityWatcher(notifier);
-
         showView();
 
         // Attach
         watcher.watch(shell, VIEW_ID);
 
-        waitForAction(latch);
+        waitForAction();
 
-        assertEquals(Action.VISIBLE, action[0]);
+        assertEquals(Action.VISIBLE, action);
 
         // Hide view
         latch = new CountDownLatch(1);
+        action = null;
 
         hideView();
 
-        waitForAction(latch);
+        waitForAction();
 
-        assertEquals(Action.HIDDEN, action[0]);
+        assertEquals(Action.HIDDEN, action);
     }
 
-    private void waitForAction(final CountDownLatch latch)
+    private void waitForAction()
             throws InterruptedException {
         if (!latch.await(TIME_OUT_MILLIS, TimeUnit.MILLISECONDS)) {
             fail("Timeout while waiting for action");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eclipse/com.redhat.thermostat.eclipse.test/src/com/redhat/thermostat/eclipse/test/views/VmClassStatViewPartTest.java	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.eclipse.test.views;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.widgets.Composite;
+import org.junit.Test;
+import org.mockito.InOrder;
+
+import com.redhat.thermostat.client.vmclassstat.core.VmClassStatController;
+import com.redhat.thermostat.client.vmclassstat.core.VmClassStatViewProvider;
+import com.redhat.thermostat.common.dao.HostRef;
+import com.redhat.thermostat.common.dao.VmClassStatDAO;
+import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.eclipse.chart.vmclassstat.Activator;
+import com.redhat.thermostat.eclipse.chart.vmclassstat.SWTVmClassStatView;
+import com.redhat.thermostat.eclipse.chart.vmclassstat.SWTVmClassStatViewProvider;
+import com.redhat.thermostat.eclipse.chart.vmclassstat.VmClassStatViewPart;
+import com.redhat.thermostat.eclipse.internal.views.RefViewPart;
+
+public class VmClassStatViewPartTest extends AbstractRefViewPartTest<VmRef> {
+
+    private SWTVmClassStatViewProvider viewProvider;
+    private VmClassStatController controller;
+
+    @Test
+    public void testSelectionAfter() throws Exception {
+        view.createPartControl(parent);
+
+        HostRef hostRef = new HostRef("TEST", "Test");
+        VmRef vmRef = new VmRef(hostRef, 0, "Test");
+        IStructuredSelection selection = mockSelection(vmRef);
+        view.selectionChanged(hostVMView, selection);
+        
+        verifyViewProvider();
+    }
+    
+    private void verifyViewProvider() {
+        InOrder order = inOrder(viewProvider, controller);
+        order.verify(viewProvider).setParent(any(Composite.class));
+        order.verify(controller).getView();
+    }
+
+    @Override
+    protected void mockController() {
+        controller = mock(VmClassStatController.class);
+        thermoView = mock(SWTVmClassStatView.class);
+
+        VmClassStatDAO classStatDao = mock(VmClassStatDAO.class);
+        viewProvider = mock(SWTVmClassStatViewProvider.class);
+        when(osgi.getService(VmClassStatDAO.class)).thenReturn(classStatDao);
+        when(osgi.getService(VmClassStatViewProvider.class)).thenReturn(viewProvider);
+
+        doReturn(controller).when(((VmClassStatViewPart) view)).createController(
+                same(classStatDao), any(VmRef.class), same(viewProvider));
+        when(controller.getView()).thenReturn(thermoView);
+    }
+
+    @Override
+    protected RefViewPart<VmRef> createViewPart() {
+        return new VmClassStatViewPart();
+    }
+
+    @Override
+    protected String getViewID() {
+        return Activator.VIEW_ID_VM_CLASS_STAT;
+    }
+
+}
--- a/eclipse/com.redhat.thermostat.eclipse.test/src/com/redhat/thermostat/eclipse/test/views/VmCpuViewPartTest.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.test/src/com/redhat/thermostat/eclipse/test/views/VmCpuViewPartTest.java	Mon Nov 19 11:40:08 2012 +0100
@@ -39,6 +39,7 @@
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.same;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -47,18 +48,24 @@
 import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.swt.widgets.Composite;
 import org.junit.Test;
+import org.mockito.InOrder;
 
 import com.redhat.thermostat.client.core.views.VmCpuViewProvider;
 import com.redhat.thermostat.client.ui.VmCpuController;
 import com.redhat.thermostat.common.dao.HostRef;
 import com.redhat.thermostat.common.dao.VmCpuStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
-import com.redhat.thermostat.eclipse.chart.common.RefViewPart;
+import com.redhat.thermostat.eclipse.ThermostatConstants;
 import com.redhat.thermostat.eclipse.chart.common.SWTVmCpuView;
+import com.redhat.thermostat.eclipse.chart.common.SWTVmCpuViewProvider;
 import com.redhat.thermostat.eclipse.chart.common.VmCpuViewPart;
+import com.redhat.thermostat.eclipse.internal.views.RefViewPart;
 
 public class VmCpuViewPartTest extends AbstractRefViewPartTest<VmRef> {
 
+    private SWTVmCpuViewProvider viewProvider;
+    private VmCpuController controller;
+
     @Test
     public void testSelectionHostRef() throws Exception {
         view.createPartControl(parent);
@@ -68,7 +75,21 @@
         view.selectionChanged(hostVMView, selection);
 
         // Ensure not created
-        verify(thermoView, never()).createControl(any(Composite.class));
+        verify(viewProvider, never()).createView();
+    }
+
+    @Test
+    public void testSelectionHostRefAfterVmRef() throws Exception {
+        HostRef hostRef = new HostRef("TEST", "Test");
+        VmRef vmRef = new VmRef(hostRef, 0, "Test");
+        mockSelection(vmRef);
+        view.createPartControl(parent);
+
+        IStructuredSelection selection = mockSelection(hostRef);
+        view.selectionChanged(hostVMView, selection);
+
+        // Ensure selection prompt shown
+        verify(view).createNoSelectionLabel();
     }
 
     @Test
@@ -80,16 +101,22 @@
         IStructuredSelection selection = mockSelection(vmRef);
         view.selectionChanged(hostVMView, selection);
 
-        verify(thermoView).createControl(any(Composite.class));
+        verifyViewProvider();
+    }
+
+    private void verifyViewProvider() {
+        InOrder order = inOrder(viewProvider, controller);
+        order.verify(viewProvider).setParent(any(Composite.class));
+        order.verify(controller).getView();
     }
 
     @Override
     protected void mockController() {
-        VmCpuController controller = mock(VmCpuController.class);
+        controller = mock(VmCpuController.class);
         thermoView = mock(SWTVmCpuView.class);
 
         VmCpuStatDAO cpuStatDao = mock(VmCpuStatDAO.class);
-        VmCpuViewProvider viewProvider = mock(VmCpuViewProvider.class);
+        viewProvider = mock(SWTVmCpuViewProvider.class);
         when(osgi.getService(VmCpuStatDAO.class)).thenReturn(cpuStatDao);
         when(osgi.getService(VmCpuViewProvider.class)).thenReturn(viewProvider);
 
@@ -103,4 +130,9 @@
         return new VmCpuViewPart();
     }
 
+    @Override
+    protected String getViewID() {
+        return ThermostatConstants.VIEW_ID_VM_CPU;
+    }
+
 }
--- a/eclipse/com.redhat.thermostat.eclipse.test/src/com/redhat/thermostat/eclipse/test/views/VmGcViewPartTest.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse.test/src/com/redhat/thermostat/eclipse/test/views/VmGcViewPartTest.java	Mon Nov 19 11:40:08 2012 +0100
@@ -39,13 +39,14 @@
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.same;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.swt.widgets.Composite;
 import org.junit.Test;
+import org.mockito.InOrder;
 
 import com.redhat.thermostat.client.core.views.VmGcViewProvider;
 import com.redhat.thermostat.client.ui.VmGcController;
@@ -53,12 +54,17 @@
 import com.redhat.thermostat.common.dao.VmGcStatDAO;
 import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
-import com.redhat.thermostat.eclipse.chart.common.RefViewPart;
+import com.redhat.thermostat.eclipse.ThermostatConstants;
 import com.redhat.thermostat.eclipse.chart.common.SWTVmGcView;
+import com.redhat.thermostat.eclipse.chart.common.SWTVmGcViewProvider;
 import com.redhat.thermostat.eclipse.chart.common.VmGcViewPart;
+import com.redhat.thermostat.eclipse.internal.views.RefViewPart;
 
 public class VmGcViewPartTest extends AbstractRefViewPartTest<VmRef> {
 
+    private SWTVmGcViewProvider viewProvider;
+    private VmGcController controller;
+
     @Test
     public void testSelectionAfter() throws Exception {
         view.createPartControl(parent);
@@ -68,17 +74,23 @@
         IStructuredSelection selection = mockSelection(vmRef);
         view.selectionChanged(hostVMView, selection);
 
-        verify(thermoView).createControl(any(Composite.class));
+        verifyViewProvider();
+    }
+
+    private void verifyViewProvider() {
+        InOrder order = inOrder(viewProvider, controller);
+        order.verify(viewProvider).setParent(any(Composite.class));
+        order.verify(controller).getView();
     }
 
     @Override
     protected void mockController() {
-        VmGcController controller = mock(VmGcController.class);
+        controller = mock(VmGcController.class);
         thermoView = mock(SWTVmGcView.class);
 
         VmMemoryStatDAO memStatDao = mock(VmMemoryStatDAO.class);
         VmGcStatDAO gcStatDao = mock(VmGcStatDAO.class);
-        VmGcViewProvider viewProvider = mock(VmGcViewProvider.class);
+        viewProvider = mock(SWTVmGcViewProvider.class);
         when(osgi.getService(VmMemoryStatDAO.class)).thenReturn(memStatDao);
         when(osgi.getService(VmGcStatDAO.class)).thenReturn(gcStatDao);
         when(osgi.getService(VmGcViewProvider.class)).thenReturn(viewProvider);
@@ -94,4 +106,9 @@
         return new VmGcViewPart();
     }
 
+    @Override
+    protected String getViewID() {
+        return ThermostatConstants.VIEW_ID_VM_GC;
+    }
+
 }
--- a/eclipse/com.redhat.thermostat.eclipse/META-INF/MANIFEST.MF	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse/META-INF/MANIFEST.MF	Mon Nov 19 11:40:08 2012 +0100
@@ -11,12 +11,15 @@
  org.eclipse.ui
 Import-Package: com.mongodb,
  com.redhat.thermostat.client.core.views,
+ com.redhat.thermostat.client.locale,
+ com.redhat.thermostat.client.ui,
  com.redhat.thermostat.common,
  com.redhat.thermostat.common.appctx,
  com.redhat.thermostat.storage.config,
  com.redhat.thermostat.common.dao,
  com.redhat.thermostat.storage.model,
  com.redhat.thermostat.storage.core,
+ com.redhat.thermostat.common.locale,
  com.redhat.thermostat.common.utils,
  com.redhat.thermostat.web.client,
  com.redhat.thermostat.web.common
@@ -25,4 +28,5 @@
  com.redhat.thermostat.eclipse.internal.controllers;x-friends:="com.redhat.thermostat.eclipse.test,com.redhat.thermostat.eclipse.test.ui",
  com.redhat.thermostat.eclipse.internal.jobs;x-friends:="com.redhat.thermostat.eclipse.test,com.redhat.thermostat.eclipse.test.ui",
  com.redhat.thermostat.eclipse.internal.model;x-friends:="com.redhat.thermostat.eclipse.test,com.redhat.thermostat.eclipse.test.ui",
- com.redhat.thermostat.eclipse.internal.views;x-friends:="com.redhat.thermostat.eclipse.test,com.redhat.thermostat.eclipse.test.ui"
+ com.redhat.thermostat.eclipse.internal.views,
+ com.redhat.thermostat.eclipse.views
--- a/eclipse/com.redhat.thermostat.eclipse/pom.xml	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse/pom.xml	Mon Nov 19 11:40:08 2012 +0100
@@ -16,25 +16,8 @@
 
   <dependencies>
     <dependency>
-      <groupId>com.redhat.thermostat</groupId>
-      <artifactId>thermostat-client-core</artifactId>
-      <version>0.5.0-SNAPSHOT</version>
-    </dependency>
-    <!-- using Web storage -->
-    <dependency>
-      <groupId>com.redhat.thermostat</groupId>
-      <artifactId>thermostat-web-client</artifactId>
-      <version>0.5.0-SNAPSHOT</version>
-    </dependency>
-    <dependency>
-      <groupId>com.redhat.thermostat</groupId>
-      <artifactId>thermostat-web-common</artifactId>
-      <version>0.5.0-SNAPSHOT</version>
-    </dependency>
-    <!-- DbService is in the launcher bundle -->
-    <dependency>
-      <groupId>com.redhat.thermostat</groupId>
-      <artifactId>thermostat-launcher</artifactId>
+      <groupId>com.redhat.thermostat.eclipse.parent</groupId>
+      <artifactId>com.redhat.thermostat.client.feature</artifactId>
       <version>0.5.0-SNAPSHOT</version>
     </dependency>
   </dependencies>
--- a/eclipse/com.redhat.thermostat.eclipse/src/com/redhat/thermostat/eclipse/SWTComponent.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse/src/com/redhat/thermostat/eclipse/SWTComponent.java	Mon Nov 19 11:40:08 2012 +0100
@@ -36,12 +36,12 @@
 
 package com.redhat.thermostat.eclipse;
 
-import org.eclipse.swt.widgets.Composite;
-
 import com.redhat.thermostat.client.core.views.UIComponent;
 
 public interface SWTComponent extends UIComponent {
+
+    public void show();
     
-    public void createControl(Composite parent);
-
-}
+    public void hide();
+    
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eclipse/com.redhat.thermostat.eclipse/src/com/redhat/thermostat/eclipse/SWTViewProvider.java	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.eclipse;
+
+import org.eclipse.swt.widgets.Composite;
+
+import com.redhat.thermostat.client.core.views.ViewProvider;
+
+public abstract class SWTViewProvider implements ViewProvider {
+    
+    private Composite parent;
+    
+    public Composite getParent() {
+        return parent;
+    }
+    
+    public void setParent(Composite parent) {
+        this.parent = parent;
+    }
+
+}
--- a/eclipse/com.redhat.thermostat.eclipse/src/com/redhat/thermostat/eclipse/internal/Activator.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse/src/com/redhat/thermostat/eclipse/internal/Activator.java	Mon Nov 19 11:40:08 2012 +0100
@@ -36,6 +36,7 @@
 
 package com.redhat.thermostat.eclipse.internal;
 
+import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.jface.resource.ImageDescriptor;
 import org.eclipse.ui.IWorkbenchPage;
@@ -43,11 +44,14 @@
 import org.eclipse.ui.plugin.AbstractUIPlugin;
 import org.osgi.framework.BundleContext;
 
+import com.redhat.thermostat.client.core.views.HostOverviewViewProvider;
 import com.redhat.thermostat.common.DbService;
 import com.redhat.thermostat.common.ThreadPoolTimerFactory;
 import com.redhat.thermostat.common.TimerFactory;
 import com.redhat.thermostat.common.appctx.ApplicationContext;
 import com.redhat.thermostat.common.utils.OSGIUtils;
+import com.redhat.thermostat.eclipse.LoggerFacility;
+import com.redhat.thermostat.eclipse.internal.views.SWTHostOverviewViewProvider;
 import com.redhat.thermostat.storage.core.ConnectionException;
 
 /**
@@ -77,10 +81,14 @@
     public void start(BundleContext context) throws Exception {
         super.start(context);
         plugin = this;
-        
+
         // Register a TimerFactory
         TimerFactory timerFactory = new ThreadPoolTimerFactory(1);
         ApplicationContext.getInstance().setTimerFactory(timerFactory);
+
+        // Register ViewProvider
+        OSGIUtils.getInstance().registerService(HostOverviewViewProvider.class,
+                new SWTHostOverviewViewProvider());
     }
 
     /*
@@ -91,11 +99,15 @@
      * )
      */
     public void stop(BundleContext context) throws Exception {
-        DbService dbService = OSGIUtils.getInstance().getService(DbService.class);
-        try {
-            dbService.disconnect();
-        } catch (ConnectionException e) {
-            e.printStackTrace();
+        DbService dbService = OSGIUtils.getInstance().getServiceAllowNull(
+                DbService.class);
+        if (dbService != null) {
+            try {
+                dbService.disconnect();
+            } catch (ConnectionException e) {
+                LoggerFacility.getInstance().log(IStatus.ERROR,
+                        "Error disconnecting from database", e);
+            }
         }
         plugin = null;
         super.stop(context);
@@ -149,9 +161,10 @@
     public static ImageDescriptor getImageDescriptor(String path) {
         return imageDescriptorFromPlugin(PLUGIN_ID, path);
     }
-    
+
     public boolean isDbConnected() {
-        DbService dbService = OSGIUtils.getInstance().getServiceAllowNull(DbService.class);
+        DbService dbService = OSGIUtils.getInstance().getServiceAllowNull(
+                DbService.class);
         return dbService != null;
     }
 
--- a/eclipse/com.redhat.thermostat.eclipse/src/com/redhat/thermostat/eclipse/internal/jobs/ConnectDbJob.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse/src/com/redhat/thermostat/eclipse/internal/jobs/ConnectDbJob.java	Mon Nov 19 11:40:08 2012 +0100
@@ -40,10 +40,10 @@
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.core.runtime.jobs.Job;
-import org.osgi.framework.BundleContext;
 
 import com.redhat.thermostat.common.DbService;
 import com.redhat.thermostat.common.DbServiceFactory;
+import com.redhat.thermostat.common.utils.OSGIUtils;
 import com.redhat.thermostat.eclipse.LoggerFacility;
 import com.redhat.thermostat.eclipse.internal.Activator;
 import com.redhat.thermostat.eclipse.internal.ConnectionConfiguration;
@@ -82,8 +82,7 @@
                 configuration.getPassword(), configuration.getDBConnectionString());
         dbService.connect();
         // register service in order to indicate that we are connected
-        BundleContext ctxt = Activator.getDefault().getBundle().getBundleContext();
-        ctxt.registerService(DbService.class, dbService, null);
+        OSGIUtils.getInstance().registerService(DbService.class, dbService);
     }
 
 }
--- a/eclipse/com.redhat.thermostat.eclipse/src/com/redhat/thermostat/eclipse/internal/views/HostOverviewViewPart.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse/src/com/redhat/thermostat/eclipse/internal/views/HostOverviewViewPart.java	Mon Nov 19 11:40:08 2012 +0100
@@ -36,372 +36,39 @@
 
 package com.redhat.thermostat.eclipse.internal.views;
 
-import org.eclipse.jface.viewers.ArrayContentProvider;
-import org.eclipse.jface.viewers.ColumnLabelProvider;
-import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.jface.viewers.TableViewer;
-import org.eclipse.jface.viewers.TableViewerColumn;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.ScrolledComposite;
-import org.eclipse.swt.graphics.Font;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.layout.RowLayout;
 import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Table;
-import org.eclipse.swt.widgets.TableColumn;
-import org.eclipse.ui.ISelectionListener;
-import org.eclipse.ui.IViewPart;
-import org.eclipse.ui.IWorkbenchPart;
-import org.eclipse.ui.PlatformUI;
-import org.eclipse.ui.part.PageBook;
-import org.eclipse.ui.part.ViewPart;
 
+import com.redhat.thermostat.client.core.views.HostOverviewViewProvider;
+import com.redhat.thermostat.client.ui.HostOverviewController;
 import com.redhat.thermostat.common.dao.HostInfoDAO;
 import com.redhat.thermostat.common.dao.HostRef;
 import com.redhat.thermostat.common.dao.NetworkInterfaceInfoDAO;
-import com.redhat.thermostat.common.dao.Ref;
-import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.utils.OSGIUtils;
-import com.redhat.thermostat.eclipse.ThermostatConstants;
-import com.redhat.thermostat.eclipse.internal.Activator;
-import com.redhat.thermostat.storage.model.HostInfo;
-import com.redhat.thermostat.storage.model.NetworkInterfaceInfo;
-
-public class HostOverviewViewPart extends ViewPart {
-
-    private static final int FIRST_COLUMN_WIDTH = 150;
-    private final String STR_UNKNOWN = "UNKNOWN";
-    private PageBook pageBook;
-    // VM page
-    private Composite vmPage;
-    private Label vmName;
-    private Composite notConnectedPage;
-    // Main compositie
-    private ScrolledComposite mainScrollPage;
-    private Label hostname;
-    private Label procModel;
-    private Label procCoreCount;
-    private Label totalMemory;
-    private TableViewer networkInterfaces;
-    private Label osName;
-    private Label osKernel;
-    private ISelection oldSelection = null;
+import com.redhat.thermostat.eclipse.SWTComponent;
+import com.redhat.thermostat.eclipse.views.HostRefViewPart;
 
-    // The listener we register with the selection service in order to listen
-    // for
-    // VmTreeView selection changes.
-    private ISelectionListener listener = new ISelectionListener() {
-        public void selectionChanged(IWorkbenchPart sourcepart,
-                ISelection selection) {
-            // only react upon hosts/vms tree changes. Then only if the selected
-            // element
-            // actually changed
-            if (sourcepart instanceof HostsVmsTreeViewPart
-                    && !selection.equals(oldSelection)) {
-                oldSelection = selection;
-                Ref ref = getRefFromSelection(selection);
-                if (Activator.getDefault().isDbConnected()) {
-                    if (ref != null) {
-                        updateText(ref);
-                        if (ref instanceof HostRef) {
-                            showPage(mainScrollPage);
-                        } else {
-                            showPage(vmPage);
-                        }
-                    }
-                } else {
-                    showPage(notConnectedPage);
-                }
-            }
-        }
-    };
-
-    private void showPage(final Control page) {
-        PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
-            @Override
-            public void run() {
-                if (page instanceof ScrolledComposite) {
-                    ((ScrolledComposite) page).getContent().pack();
-                } else {
-                    page.pack();
-                }
-                pageBook.showPage(page);
-            }
-        });
-    }
+public class HostOverviewViewPart extends HostRefViewPart {
 
     @Override
-    public void createPartControl(Composite parent) {
-        pageBook = new PageBook(parent, SWT.NONE);
-        mainScrollPage = new ScrolledComposite(pageBook, SWT.NONE
-                | SWT.V_SCROLL | SWT.H_SCROLL);
-        Composite main = new Composite(mainScrollPage, SWT.NONE);
-        mainScrollPage.setContent(main);
-        vmPage = new Composite(pageBook, SWT.NONE);
-
-        vmName = new Label(vmPage, SWT.NONE);
-        vmPage.setLayout(new RowLayout());
-
-        notConnectedPage = new Composite(pageBook, SWT.NONE);
-        notConnectedPage.setLayout(new RowLayout());
-
-        // ----------------------------------------
-        // Not connected page
-        // ----------------------------------------
-        Label notConn = new Label(notConnectedPage, SWT.NONE);
-        notConn.setText("Not connected to storage");
-
-        // ----------------------------------------
-        // Main overview page
-        // ----------------------------------------
-        main.setLayout(new GridLayout());
-
-        // Basics
-        Label lblBasics = new Label(main, SWT.NONE);
-        lblBasics.setText("Basics"); // TODO: Externalize
-        Font stdFont = lblBasics.getFont();
-        Font boldFont = new Font(stdFont.getDevice(),
-                stdFont.getFontData()[0].getName(),
-                stdFont.getFontData()[0].getHeight(), SWT.BOLD);
-        lblBasics.setFont(boldFont);
-        Composite basicsComps = new Composite(main, SWT.NONE);
-        GridLayout gridlayout = new GridLayout(2, false);
-        basicsComps.setLayout(gridlayout);
-        Label lblHostName = new Label(basicsComps, SWT.NONE);
-        lblHostName.setText("Hostname");
-        GridData hostNameGridData = new GridData(SWT.RIGHT, SWT.CENTER, false,
-                false);
-        hostNameGridData.widthHint = FIRST_COLUMN_WIDTH;
-        lblHostName.setLayoutData(hostNameGridData);
-        hostname = new Label(basicsComps, SWT.NONE);
-        hostname.setText(STR_UNKNOWN);
-        hostname.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, true));
+    protected SWTComponent createControllerView(HostRef ref, Composite top) {
+        HostInfoDAO hostInfoDAO = OSGIUtils.getInstance().getService(
+                HostInfoDAO.class);
+        NetworkInterfaceInfoDAO networkInfoDAO = OSGIUtils.getInstance()
+                .getService(NetworkInterfaceInfoDAO.class);
+        SWTHostOverviewViewProvider provider = (SWTHostOverviewViewProvider) OSGIUtils
+                .getInstance().getService(HostOverviewViewProvider.class);
+        provider.setParent(top);
 
-        // Hardware
-        Label lblHardware = new Label(main, SWT.NONE);
-        lblHardware.setText("Hardware");
-        lblHardware.setFont(boldFont);
-        Composite hardwareComps = new Composite(main, SWT.NONE);
-        hardwareComps.setLayout(gridlayout);
-        Label lblProcModel = new Label(hardwareComps, SWT.NONE);
-        lblProcModel.setText("Processor Model");
-        GridData procModelGridData = new GridData(SWT.RIGHT, SWT.CENTER, false,
-                false);
-        procModelGridData.widthHint = FIRST_COLUMN_WIDTH;
-        lblProcModel.setLayoutData(procModelGridData);
-        procModel = new Label(hardwareComps, SWT.NONE);
-        procModel.setText(STR_UNKNOWN);
-        procModel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, true));
-        Label lblProcCount = new Label(hardwareComps, SWT.NONE);
-        lblProcCount.setText("Processor Count");
-        GridData procCountGridData = new GridData(SWT.RIGHT, SWT.CENTER, false,
-                false);
-        procCountGridData.widthHint = FIRST_COLUMN_WIDTH;
-        lblProcCount.setLayoutData(procCountGridData);
-        procCoreCount = new Label(hardwareComps, SWT.NONE);
-        procCoreCount.setText(STR_UNKNOWN);
-        procCoreCount.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true,
-                true));
-        Label lblTotalMemory = new Label(hardwareComps, SWT.NONE);
-        lblTotalMemory.setText("Total Memory");
-        GridData totalMemGridData = new GridData(SWT.RIGHT, SWT.CENTER, false,
-                false);
-        totalMemGridData.widthHint = FIRST_COLUMN_WIDTH;
-        lblTotalMemory.setLayoutData(totalMemGridData);
-        lblTotalMemory.pack();
-        totalMemory = new Label(hardwareComps, SWT.NONE);
-        totalMemory.setText(STR_UNKNOWN);
-        totalMemory
-                .setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, true));
-        Label lblNetwork = new Label(hardwareComps, SWT.NONE);
-        lblNetwork.setText("Network");
-        GridData networkLayout = new GridData(SWT.RIGHT, SWT.TOP, true, true);
-        networkLayout.widthHint = FIRST_COLUMN_WIDTH;
-        lblNetwork.setLayoutData(networkLayout);
-        networkInterfaces = new TableViewer(hardwareComps, SWT.BORDER);
-        createNetworkTableViewer(networkInterfaces);
-
-        // Software
-        Label lblSoftware = new Label(main, SWT.NONE);
-        lblSoftware.setText("Software");
-        lblSoftware.setFont(boldFont);
-        Composite softwareComps = new Composite(main, SWT.NONE);
-        softwareComps.setLayout(gridlayout);
-        Label lblOsName = new Label(softwareComps, SWT.NONE);
-        lblOsName.setText("OS Name");
-        GridData osNameGridData = new GridData(SWT.RIGHT, SWT.CENTER, false,
-                false);
-        osNameGridData.widthHint = FIRST_COLUMN_WIDTH;
-        lblOsName.setLayoutData(osNameGridData);
-        osName = new Label(softwareComps, SWT.NONE);
-        osName.setText(STR_UNKNOWN);
-        osName.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, true));
-        Label lblKernel = new Label(softwareComps, SWT.NONE);
-        lblKernel.setText("OS Kernel");
-        GridData osKernelGridData = new GridData(SWT.RIGHT, SWT.CENTER, false,
-                false);
-        osKernelGridData.widthHint = FIRST_COLUMN_WIDTH;
-        lblKernel.setLayoutData(osKernelGridData);
-        osKernel = new Label(softwareComps, SWT.NONE);
-        osKernel.setText(STR_UNKNOWN);
-        osKernel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, true));
-
-        // Listen for VMtree changes
-        getSite().getWorkbenchWindow().getSelectionService()
-        .addSelectionListener(listener);
-        if (Activator.getDefault().isDbConnected()) {
-        	// Explicitly get the selected element from the VmsTreeViewPart
-        	IViewPart part = getSite().getWorkbenchWindow().getActivePage().findView(ThermostatConstants.VIEW_ID_HOST_VM);
-        	if (part != null && part instanceof HostsVmsTreeViewPart) {
-        		ISelection selection = part.getSite().getSelectionProvider().getSelection();
-        		Ref ref = getRefFromSelection(selection);
-        		if (ref != null) {
-        			updateText(ref);
-        			if (ref instanceof HostRef) {
-        				showPage(mainScrollPage);
-        			} else {
-        				showPage(vmPage);
-        			}
-        		} else {
-        			// FIXME: probably want to show something else, e.g. select x in
-        			// VM tree
-        			showPage(notConnectedPage);
-        		}
-        	} else {
-        		showPage(notConnectedPage);
-        	}
-        } else {
-            showPage(notConnectedPage);
-        }
+        HostOverviewController controller = createController(ref, hostInfoDAO,
+                networkInfoDAO, provider);
+        return (SWTComponent) controller.getView();
     }
 
-    @Override
-    public void setFocus() {
-        pageBook.setFocus();
-    }
-
-    private void updateText(Ref ref) {
-        if (ref instanceof HostRef) {
-            updateText((HostRef) ref);
-        } else {
-            updateText((VmRef) ref);
-        }
-    }
-
-    private void updateText(final HostRef hostRef) {
-        HostInfoDAO hostInfoDAO = OSGIUtils.getInstance().getService(
-                HostInfoDAO.class);
-        final HostInfo hostInfo = hostInfoDAO.getHostInfo(hostRef);
-        final NetworkInterfaceInfoDAO networkInfoDAO = OSGIUtils.getInstance()
-                .getService(NetworkInterfaceInfoDAO.class);
-        PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
-            @Override
-            public void run() {
-                hostname.setText(hostRef.getHostName());
-                osName.setText(hostInfo.getOsName());
-                procModel.setText(hostInfo.getCpuModel());
-                procCoreCount.setText(Integer.toString(hostInfo.getCpuCount()));
-                osKernel.setText(hostInfo.getOsKernel());
-                totalMemory.setText(Long.toString(hostInfo.getTotalMemory()));
-                // set content for the network iface table
-                networkInterfaces.setInput(networkInfoDAO.getNetworkInterfaces(
-                        hostRef).toArray());
-            }
-        });
-    }
-
-    private void updateText(final VmRef vmRef) {
-        PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
-            @Override
-            public void run() {
-                vmName.setText(vmRef.getName());
-            }
-        });
-    }
-
-    private Ref getRefFromSelection(ISelection selection) {
-        if (selection instanceof IStructuredSelection) {
-            IStructuredSelection ss = (IStructuredSelection) selection;
-            // FIXME: hostsVms tree should only allow single selections
-            for (Object item : ss.toArray()) {
-                if (item instanceof Ref) {
-                    return (Ref) item;
-                }
-            }
-        }
-        return null;
+    public HostOverviewController createController(HostRef ref,
+            HostInfoDAO hostInfoDAO, NetworkInterfaceInfoDAO networkInfoDAO,
+            HostOverviewViewProvider provider) {
+        return new HostOverviewController(hostInfoDAO, networkInfoDAO, ref,
+                provider);
     }
 
-    @Override
-    public void dispose() {
-        // important: We need do unregister our listener when the view is
-        // disposed
-        getSite().getWorkbenchWindow().getSelectionService()
-                .removeSelectionListener(listener);
-        super.dispose();
-    }
-
-    private void createNetworkTableViewer(TableViewer viewer) {
-        createColumns(viewer.getControl().getParent(), viewer);
-        final Table table = viewer.getTable();
-        table.setHeaderVisible(true);
-        table.setLinesVisible(true);
-
-        viewer.setContentProvider(new ArrayContentProvider());
-        // create empty table
-        viewer.setInput(new Object[0]);
-    }
-
-    // This will create the columns for the table
-    private void createColumns(final Composite parent, final TableViewer viewer) {
-        String[] titles = { "Interface", "IPv4 Address", "IPv6 Address" };
-        int[] bounds = { 80, 150, 150 };
-
-        // First column is iface name
-        TableViewerColumn col = createTableViewerColumn(viewer, titles[0],
-                bounds[0], 0);
-        col.setLabelProvider(new ColumnLabelProvider() {
-            @Override
-            public String getText(Object element) {
-                NetworkInterfaceInfo iface = (NetworkInterfaceInfo) element;
-                return iface.getInterfaceName();
-            }
-        });
-
-        // Second column is IPv4
-        col = createTableViewerColumn(viewer, titles[1], bounds[1], 1);
-        col.setLabelProvider(new ColumnLabelProvider() {
-            @Override
-            public String getText(Object element) {
-                NetworkInterfaceInfo iface = (NetworkInterfaceInfo) element;
-                return iface.getIp4Addr();
-            }
-        });
-
-        // IPv6
-        col = createTableViewerColumn(viewer, titles[2], bounds[2], 2);
-        col.setLabelProvider(new ColumnLabelProvider() {
-            @Override
-            public String getText(Object element) {
-                NetworkInterfaceInfo iface = (NetworkInterfaceInfo) element;
-                return iface.getIp6Addr();
-            }
-        });
-    }
-
-    private TableViewerColumn createTableViewerColumn(TableViewer viewer,
-            String title, int bound, final int colNumber) {
-        final TableViewerColumn viewerColumn = new TableViewerColumn(viewer,
-                SWT.NONE);
-        final TableColumn column = viewerColumn.getColumn();
-        column.setText(title);
-        column.setWidth(bound);
-        column.setResizable(true);
-        column.setMoveable(false);
-        return viewerColumn;
-    }
 }
--- a/eclipse/com.redhat.thermostat.eclipse/src/com/redhat/thermostat/eclipse/internal/views/HostsVmsTreeViewPart.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/com.redhat.thermostat.eclipse/src/com/redhat/thermostat/eclipse/internal/views/HostsVmsTreeViewPart.java	Mon Nov 19 11:40:08 2012 +0100
@@ -82,7 +82,9 @@
 
     public HostsVmsTreeViewPart() {
         // FIXME: Get these values from preferences
-        ConnectionConfiguration configuration = new ConnectionConfiguration("dummyUser", "dummyPassword", "mongodb://127.0.0.1:27518");
+        // This circumvents webservice (uses mongo directly). If you want to use webstorage use something like:
+        // ConnectionConfiguration configuration = new ConnectionConfiguration("", "", "http://127.0.0.1:8082");
+        ConnectionConfiguration configuration = new ConnectionConfiguration("", "", "mongodb://127.0.0.1:27518");
         Job connectJob = new ConnectDbJob(
                 "Connecting to Thermostat storage...", configuration);
         connectJob.setSystem(true);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eclipse/com.redhat.thermostat.eclipse/src/com/redhat/thermostat/eclipse/internal/views/RefViewPart.java	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.eclipse.internal.views;
+
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.ISelectionListener;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.part.ViewPart;
+
+import com.redhat.thermostat.common.dao.Ref;
+import com.redhat.thermostat.eclipse.SWTComponent;
+import com.redhat.thermostat.eclipse.ThermostatConstants;
+
+public abstract class RefViewPart<T extends Ref> extends ViewPart implements
+        ISelectionListener {
+
+    protected Composite top;
+
+    private Composite parent;
+    private IStructuredSelection currentSelection;
+
+    public RefViewPart() {
+        super();
+    }
+
+    @Override
+    public void createPartControl(Composite parent) {
+        this.parent = parent;
+
+        createComposite();
+        
+        getWorkbenchWindow().getSelectionService().addSelectionListener(this);
+
+        // Check for an existing selection
+        boolean selected = false;
+        IViewPart part = getWorkbenchWindow().getActivePage().findView(
+                ThermostatConstants.VIEW_ID_HOST_VM);
+        if (part != null) {
+            ISelection selection = part.getSite().getSelectionProvider()
+                    .getSelection();
+            if (selection instanceof IStructuredSelection
+                    && !selection.isEmpty()) {
+                currentSelection = (IStructuredSelection) selection;
+                T ref = handleSelection(selection);
+                if (ref != null) {
+                    createView(ref);
+                    selected = true;
+                }
+            }
+        }
+        if (!selected) {
+            createNoSelectionLabel();
+        }
+    }
+
+    public void createNoSelectionLabel() {
+        Label noHost = new Label(top, SWT.NONE);
+        noHost.setText(getNoSelectionMessage());
+    }
+
+    public IWorkbenchWindow getWorkbenchWindow() {
+        return PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+    }
+
+    @Override
+    public void setFocus() {
+        parent.setFocus();
+    }
+    
+    protected abstract SWTComponent createControllerView(T ref, Composite parent);
+
+    protected abstract T getRefFromSelection(Object selection);
+
+    protected abstract String getNoSelectionMessage();
+
+    @Override
+    public void selectionChanged(IWorkbenchPart part, ISelection selection) {
+        // We must have received createPartControl
+        if (parent != null && !parent.isDisposed()) {
+            // Check if a HostRef has been selected
+            if (part.getSite().getId()
+                    .equals(ThermostatConstants.VIEW_ID_HOST_VM)) {
+                if (selection instanceof IStructuredSelection
+                        && !selection.isEmpty()
+                        && !selection.equals(currentSelection)) {
+                    currentSelection = (IStructuredSelection) selection;
+                    T ref = handleSelection(selection);
+                    // Replace the existing view
+                    top.dispose();
+                    createComposite();
+
+                    if (ref != null) {
+                        createView(ref);
+                    } else {
+                        // Prompt the user to select a valid ref
+                        createNoSelectionLabel();
+                    }
+                    parent.layout();
+                }
+            }
+        }
+    }
+
+    private T handleSelection(ISelection selection) {
+        Object selectedElement = ((IStructuredSelection) selection)
+                .getFirstElement();
+        return getRefFromSelection(selectedElement);
+    }
+
+    private void createComposite() {
+        top = new Composite(parent, SWT.NONE);
+        top.setLayout(new GridLayout());
+        top.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+    }
+
+    private void createView(T ref) {
+        SWTComponent view = createControllerView(ref, top);
+        ViewVisibilityWatcher watcher = new ViewVisibilityWatcher(view);
+        watcher.watch(top, getSite().getId());
+    }
+    
+    
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eclipse/com.redhat.thermostat.eclipse/src/com/redhat/thermostat/eclipse/internal/views/SWTHostOverviewView.java	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,373 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.eclipse.internal.views;
+
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.CellLabelProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.ui.PlatformUI;
+
+import com.redhat.thermostat.client.core.views.HostOverviewView;
+import com.redhat.thermostat.client.locale.LocaleResources;
+import com.redhat.thermostat.common.locale.Translate;
+import com.redhat.thermostat.eclipse.SWTComponent;
+import com.redhat.thermostat.eclipse.ThermostatConstants;
+
+public class SWTHostOverviewView extends HostOverviewView implements
+        SWTComponent {
+    private static final String CLASS_NAME = SWTHostOverviewView.class.getSimpleName();
+    public static final String TEST_ID_HOSTNAME = CLASS_NAME + ".hostname";
+    public static final String TEST_ID_PROC_MODEL = CLASS_NAME + ".procModel";
+    public static final String TEST_ID_PROC_CORE_COUNT = CLASS_NAME + ".procCoreCount";
+    public static final String TEST_ID_TOTAL_MEMORY = CLASS_NAME + ".totalMemory";
+    public static final String TEST_ID_NETWORK_INTERFACES = CLASS_NAME + ".networkInterfaces";
+    public static final String TEST_ID_OS_NAME = CLASS_NAME + ".osName";
+    public static final String TEST_ID_OS_KERNEL = CLASS_NAME + ".osKernel";
+    
+    private static final Translate<LocaleResources> translator = LocaleResources
+            .createLocalizer();
+    private static final int FIRST_COLUMN_WIDTH = 150;
+    private static final String STR_UNKNOWN = "UNKNOWN";
+    private static final int NUM_TABLE_ROWS = 5;
+    private static int[] TABLE_COLUMN_WIDTHS = { 80, 150, 300 };
+
+    private Label hostname;
+    private Label procModel;
+    private Label procCoreCount;
+    private Label totalMemory;
+    private TableViewer networkInterfaces;
+    private Label osName;
+    private Label osKernel;
+
+    public SWTHostOverviewView(Composite parent) {
+        Composite top = new Composite(parent, SWT.NONE);
+        top.setLayout(new GridLayout());
+        top.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+        // Basics
+        Label lblBasics = new Label(top, SWT.NONE);
+        lblBasics.setText(translator
+                .localize(LocaleResources.HOST_OVERVIEW_SECTION_BASICS));
+        Font stdFont = lblBasics.getFont();
+        Font boldFont = new Font(stdFont.getDevice(),
+                stdFont.getFontData()[0].getName(),
+                stdFont.getFontData()[0].getHeight(), SWT.BOLD);
+        lblBasics.setFont(boldFont);
+        Composite basicsComps = new Composite(top, SWT.NONE);
+        GridLayout gridlayout = new GridLayout(2, false);
+        basicsComps.setLayout(gridlayout);
+        basicsComps.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true,
+                false));
+        Label lblHostName = new Label(basicsComps, SWT.NONE);
+        lblHostName.setText(translator
+                .localize(LocaleResources.HOST_INFO_HOSTNAME));
+        GridData hostNameGridData = new GridData(SWT.FILL, SWT.CENTER, false,
+                false);
+        hostNameGridData.widthHint = FIRST_COLUMN_WIDTH;
+        lblHostName.setLayoutData(hostNameGridData);
+        hostname = new Label(basicsComps, SWT.NONE);
+        hostname.setData(ThermostatConstants.TEST_TAG, TEST_ID_HOSTNAME);
+        hostname.setText(STR_UNKNOWN);
+        hostname.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+
+        // Hardware
+        Label lblHardware = new Label(top, SWT.NONE);
+        lblHardware.setText(translator
+                .localize(LocaleResources.HOST_OVERVIEW_SECTION_HARDWARE));
+        lblHardware.setFont(boldFont);
+        Composite hardwareComps = new Composite(top, SWT.NONE);
+        hardwareComps.setLayout(gridlayout);
+        hardwareComps.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true,
+                false));
+        Label lblProcModel = new Label(hardwareComps, SWT.NONE);
+        lblProcModel.setText(translator
+                .localize(LocaleResources.HOST_INFO_CPU_MODEL));
+        GridData procModelGridData = new GridData(SWT.FILL, SWT.CENTER, false,
+                false);
+        procModelGridData.widthHint = FIRST_COLUMN_WIDTH;
+        lblProcModel.setLayoutData(procModelGridData);
+        procModel = new Label(hardwareComps, SWT.NONE);
+        procModel.setData(ThermostatConstants.TEST_TAG, TEST_ID_PROC_MODEL);
+        procModel.setText(STR_UNKNOWN);
+        procModel
+                .setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+        Label lblProcCount = new Label(hardwareComps, SWT.NONE);
+        lblProcCount.setText(translator
+                .localize(LocaleResources.HOST_INFO_CPU_COUNT));
+        GridData procCountGridData = new GridData(SWT.FILL, SWT.CENTER, false,
+                false);
+        procCountGridData.widthHint = FIRST_COLUMN_WIDTH;
+        lblProcCount.setLayoutData(procCountGridData);
+        procCoreCount = new Label(hardwareComps, SWT.NONE);
+        procCoreCount.setData(ThermostatConstants.TEST_TAG, TEST_ID_PROC_CORE_COUNT);
+        procCoreCount.setText(STR_UNKNOWN);
+        procCoreCount.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true,
+                false));
+        Label lblTotalMemory = new Label(hardwareComps, SWT.NONE);
+        lblTotalMemory.setText(translator
+                .localize(LocaleResources.HOST_INFO_MEMORY_TOTAL));
+        GridData totalMemGridData = new GridData(SWT.FILL, SWT.CENTER, false,
+                false);
+        totalMemGridData.widthHint = FIRST_COLUMN_WIDTH;
+        lblTotalMemory.setLayoutData(totalMemGridData);
+        totalMemory = new Label(hardwareComps, SWT.NONE);
+        totalMemory.setData(ThermostatConstants.TEST_TAG, TEST_ID_TOTAL_MEMORY);
+        totalMemory.setText(STR_UNKNOWN);
+        totalMemory.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true,
+                false));
+        Label lblNetwork = new Label(hardwareComps, SWT.NONE);
+        lblNetwork.setText(translator
+                .localize(LocaleResources.HOST_INFO_NETWORK));
+        GridData networkLayout = new GridData(SWT.FILL, SWT.TOP, false, false);
+        networkLayout.widthHint = FIRST_COLUMN_WIDTH;
+        lblNetwork.setLayoutData(networkLayout);
+        networkInterfaces = new TableViewer(hardwareComps, SWT.BORDER);
+        networkInterfaces.getTable().setData(ThermostatConstants.TEST_TAG, TEST_ID_NETWORK_INTERFACES);
+        createNetworkTableViewer();
+
+        // Software
+        Label lblSoftware = new Label(top, SWT.NONE);
+        lblSoftware.setText(translator
+                .localize(LocaleResources.HOST_OVERVIEW_SECTION_SOFTWARE));
+        lblSoftware.setFont(boldFont);
+        Composite softwareComps = new Composite(top, SWT.NONE);
+        softwareComps.setLayout(gridlayout);
+        softwareComps.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true,
+                false));
+        Label lblOsName = new Label(softwareComps, SWT.NONE);
+        lblOsName.setText(translator
+                .localize(LocaleResources.HOST_INFO_OS_NAME));
+        GridData osNameGridData = new GridData(SWT.FILL, SWT.CENTER, false,
+                false);
+        osNameGridData.widthHint = FIRST_COLUMN_WIDTH;
+        lblOsName.setLayoutData(osNameGridData);
+        osName = new Label(softwareComps, SWT.NONE);
+        osName.setData(ThermostatConstants.TEST_TAG, TEST_ID_OS_NAME);
+        osName.setText(STR_UNKNOWN);
+        osName.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+        Label lblKernel = new Label(softwareComps, SWT.NONE);
+        lblKernel.setText(translator
+                .localize(LocaleResources.HOST_INFO_OS_KERNEL));
+        GridData osKernelGridData = new GridData(SWT.FILL, SWT.CENTER, false,
+                false);
+        osKernelGridData.widthHint = FIRST_COLUMN_WIDTH;
+        lblKernel.setLayoutData(osKernelGridData);
+        osKernel = new Label(softwareComps, SWT.NONE);
+        osKernel.setData(ThermostatConstants.TEST_TAG, TEST_ID_OS_KERNEL);
+        osKernel.setText(STR_UNKNOWN);
+        osKernel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+    }
+
+    @Override
+    public void setHostName(final String newHostName) {
+        PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
+            @Override
+            public void run() {
+                if (!hostname.isDisposed()) {
+                    hostname.setText(newHostName);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void setCpuModel(final String newCpuModel) {
+        PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
+            @Override
+            public void run() {
+                if (!procModel.isDisposed()) {
+                    procModel.setText(newCpuModel);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void setCpuCount(final String newCpuCount) {
+        PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
+            @Override
+            public void run() {
+                if (!procCoreCount.isDisposed()) {
+                    procCoreCount.setText(newCpuCount);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void setTotalMemory(final String newTotalMemory) {
+        PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
+            @Override
+            public void run() {
+                if (!totalMemory.isDisposed()) {
+                    totalMemory.setText(newTotalMemory);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void setOsName(final String newOsName) {
+        PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
+            @Override
+            public void run() {
+                if (!osName.isDisposed()) {
+                    osName.setText(newOsName);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void setOsKernel(final String newOsKernel) {
+        PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
+            @Override
+            public void run() {
+                if (!osKernel.isDisposed()) {
+                    osKernel.setText(newOsKernel);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void setNetworkTableColumns(final Object[] columns) {
+        PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
+            @Override
+            public void run() {
+                if (!networkInterfaces.getTable().isDisposed()) {
+                    for (int i = 0; i < columns.length; i++) {
+                        Object column = columns[i];
+                        createTableViewerColumn(column.toString(),
+                                TABLE_COLUMN_WIDTHS[i]);
+                        networkInterfaces.getTable().getParent().layout();
+                    }
+                }
+            }
+        });
+    }
+
+    @Override
+    public void setInitialNetworkTableData(final Object[][] table) {
+        PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
+            @Override
+            public void run() {
+                if (!networkInterfaces.getTable().isDisposed()) {
+                    networkInterfaces.setInput(table);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void updateNetworkTableData(final int row, final int column,
+            final String data) {
+        PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
+            @Override
+            public void run() {
+                if (!networkInterfaces.getTable().isDisposed()) {
+                    Object[][] input = (Object[][]) networkInterfaces
+                            .getInput();
+                    input[row][column] = data;
+                    networkInterfaces.refresh();
+                }
+            }
+        });
+    }
+
+    private void createNetworkTableViewer() {
+        final Table table = networkInterfaces.getTable();
+        table.setHeaderVisible(true);
+        table.setLinesVisible(true);
+        table.setLayout(new GridLayout());
+        GridData tableGridData = new GridData();
+        int height = NUM_TABLE_ROWS
+                * networkInterfaces.getTable().getItemHeight();
+        tableGridData.heightHint = height;
+        table.setLayoutData(tableGridData);
+
+        networkInterfaces.setContentProvider(new ArrayContentProvider());
+
+        // create empty table
+        networkInterfaces.setInput(new Object[0]);
+    }
+
+    private TableViewerColumn createTableViewerColumn(String title, int bound) {
+        final TableViewerColumn viewerColumn = new TableViewerColumn(
+                networkInterfaces, SWT.NONE);
+        final TableColumn column = viewerColumn.getColumn();
+        column.setText(title);
+        column.setWidth(bound);
+        column.setResizable(true);
+        column.setMoveable(false);
+
+        viewerColumn.setLabelProvider(new CellLabelProvider() {
+
+            @Override
+            public void update(ViewerCell cell) {
+                int idx = cell.getColumnIndex();
+                Object[] array = (Object[]) cell.getElement();
+                Object element = array[idx];
+                if (element != null) {
+                    cell.setText(element.toString());
+                }
+            }
+        });
+        return viewerColumn;
+    }
+
+    @Override
+    public void show() {
+        notifier.fireAction(Action.VISIBLE);
+    }
+
+    @Override
+    public void hide() {
+        notifier.fireAction(Action.HIDDEN);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eclipse/com.redhat.thermostat.eclipse/src/com/redhat/thermostat/eclipse/internal/views/SWTHostOverviewViewProvider.java	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.eclipse.internal.views;
+
+import com.redhat.thermostat.client.core.views.HostOverviewView;
+import com.redhat.thermostat.client.core.views.HostOverviewViewProvider;
+import com.redhat.thermostat.eclipse.SWTViewProvider;
+
+public class SWTHostOverviewViewProvider extends SWTViewProvider implements HostOverviewViewProvider {
+
+    @Override
+    public HostOverviewView createView() {
+        return new SWTHostOverviewView(getParent());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eclipse/com.redhat.thermostat.eclipse/src/com/redhat/thermostat/eclipse/internal/views/ViewVisibilityWatcher.java	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.eclipse.internal.views;
+
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.IPartListener2;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPartReference;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+import com.redhat.thermostat.eclipse.SWTComponent;
+
+public class ViewVisibilityWatcher {
+    
+    private SWTComponent component;
+    private boolean visible;
+
+    public ViewVisibilityWatcher(SWTComponent component) {
+        this.component = component;
+        this.visible = false;
+    }
+    
+    public void watch(Composite parent, final String viewID) {
+        // Check if the view is currently visible
+        IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+        IViewPart view = activePage.findView(viewID);
+        if (activePage.isPartVisible(view)) {
+            visible = true;
+            component.show();
+        }
+        
+        // Register listener for visibility updates on the Eclipse view
+        final IPartListener2 partListener = new IPartListener2() {
+            
+            @Override
+            public void partVisible(IWorkbenchPartReference partRef) {
+                // The workbench fires a visible event when the view first takes
+                // focus, even if it was already on top
+                if (!visible && viewID.equals(partRef.getId())) {
+                    component.show();
+                    visible = true;
+                }
+            }
+            
+            @Override
+            public void partHidden(IWorkbenchPartReference partRef) {
+                if (visible && viewID.equals(partRef.getId())) {
+                    component.hide();
+                    visible = false;
+                }
+            }
+
+            @Override
+            public void partOpened(IWorkbenchPartReference partRef) {
+            }
+            
+            @Override
+            public void partInputChanged(IWorkbenchPartReference partRef) {
+            }
+            
+            @Override
+            public void partDeactivated(IWorkbenchPartReference partRef) {
+            }
+            
+            @Override
+            public void partClosed(IWorkbenchPartReference partRef) {
+            }
+            
+            @Override
+            public void partBroughtToTop(IWorkbenchPartReference partRef) {
+            }
+            
+            @Override
+            public void partActivated(IWorkbenchPartReference partRef) {
+            }
+        };
+        
+        PlatformUI.getWorkbench().getActiveWorkbenchWindow().getPartService().addPartListener(partListener);
+        
+        parent.addDisposeListener(new DisposeListener() {
+            
+            @Override
+            public void widgetDisposed(DisposeEvent e) {
+                PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
+                    
+                    @Override
+                    public void run() {
+                        IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+                        if (window != null) {
+                            window.getPartService().removePartListener(partListener);
+                        }
+                    }
+                });
+            }
+        });
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eclipse/com.redhat.thermostat.eclipse/src/com/redhat/thermostat/eclipse/views/HostRefViewPart.java	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.eclipse.views;
+
+import com.redhat.thermostat.common.dao.HostRef;
+import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.eclipse.internal.views.RefViewPart;
+
+public abstract class HostRefViewPart extends RefViewPart<HostRef> {
+
+    public HostRefViewPart() {
+        super();
+    }
+
+    @Override
+    protected HostRef getRefFromSelection(Object selection) {
+        HostRef ref = null;
+        if (selection instanceof HostRef) {
+            ref = (HostRef) selection;
+        }
+        else if (selection instanceof VmRef) {
+            ref = ((VmRef) selection).getAgent();
+        }
+        return ref;
+    }
+
+    @Override
+    protected String getNoSelectionMessage() {
+        return "No host selected";
+    }
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eclipse/com.redhat.thermostat.eclipse/src/com/redhat/thermostat/eclipse/views/VmRefViewPart.java	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.eclipse.views;
+
+import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.eclipse.internal.views.RefViewPart;
+
+public abstract class VmRefViewPart extends RefViewPart<VmRef> {
+
+    public VmRefViewPart() {
+        super();
+    }
+
+    @Override
+    protected VmRef getRefFromSelection(Object selection) {
+        VmRef ref = null;
+        if (selection instanceof VmRef) {
+            ref = (VmRef) selection;
+        }
+        return ref;
+    }
+
+    @Override
+    protected String getNoSelectionMessage() {
+        return "No VM selected";
+    }
+
+}
--- a/eclipse/composite-repo/compositeArtifacts.xml	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/composite-repo/compositeArtifacts.xml	Mon Nov 19 11:40:08 2012 +0100
@@ -2,11 +2,10 @@
 <?compositeMetadataRepository version='1.0.0'?>
 <repository name='Thermostat Eclipse Composite p2 repository'
     type='org.eclipse.equinox.internal.p2.artifact.repository.CompositeArtifactRepository' version='1.0.0'>
- <children size='3'>
+  <children size='2'>
+    <!-- Thermostat Eclipse plug-ins/features -->
     <child location='thermostat-eclipse'/>
-    <!-- Thermostat core bundles with p2 metadata -->
+    <!-- Thermostat core bundles/features with p2 metadata -->
     <child location='thermostat-core-p2'/>
-    <!-- Eclipse Orbit repo for mongodb and lucene -->
-    <child location='http://download.eclipse.org/tools/orbit/downloads/drops/R20120526062928/repository/' />
   </children>
 </repository>
--- a/eclipse/composite-repo/compositeContent.xml	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/composite-repo/compositeContent.xml	Mon Nov 19 11:40:08 2012 +0100
@@ -2,11 +2,10 @@
 <?compositeMetadataRepository version='1.0.0'?>
 <repository name='Thermostat Eclipse Composite p2 repository'
     type='org.eclipse.equinox.internal.p2.metadata.repository.CompositeMetadataRepository' version='1.0.0'>
- <children size='3'>
+  <children size='2'>
+    <!-- Thermostat Eclipse plug-ins/features -->
     <child location='thermostat-eclipse'/>
-    <!-- Thermostat core bundles with p2 metadata -->
+    <!-- Thermostat core bundles/features with p2 metadata -->
     <child location='thermostat-core-p2'/>
-    <!-- Eclipse Orbit repo for mongodb and lucene -->
-    <child location='http://download.eclipse.org/tools/orbit/downloads/drops/R20120526062928/repository/' />
   </children>
 </repository>
--- a/eclipse/composite-repo/pom.xml	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/composite-repo/pom.xml	Mon Nov 19 11:40:08 2012 +0100
@@ -102,7 +102,7 @@
       <groupId>com.redhat.thermostat.eclipse.parent</groupId>
       <artifactId>com.redhat.thermostat.eclipse.core-p2-repo</artifactId>
       <version>${project.version}</version>
-      <type>pom</type>
+      <type>eclipse-repository</type>
     </dependency>
   </dependencies>
 
--- a/eclipse/core-p2-repository/pom.xml	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/core-p2-repository/pom.xml	Mon Nov 19 11:40:08 2012 +0100
@@ -18,6 +18,7 @@
       <groupId>com.redhat.thermostat.eclipse.parent</groupId>
       <artifactId>com.redhat.thermostat.client.feature</artifactId>
       <version>0.5.0-SNAPSHOT</version>
+      <type>eclipse-feature</type>
     </dependency>
   </dependencies>
 
--- a/eclipse/pom.xml	Mon Nov 19 11:35:17 2012 +0100
+++ b/eclipse/pom.xml	Mon Nov 19 11:40:08 2012 +0100
@@ -92,19 +92,11 @@
     <module>com.redhat.thermostat.eclipse.boot</module>
     <module>com.redhat.thermostat.eclipse</module>
     <module>com.redhat.thermostat.eclipse.chart.common</module>
-    <module>com.redhat.thermostat.eclipse.test</module>
-    <module>com.redhat.thermostat.eclipse.test.ui</module>
+    <module>com.redhat.thermostat.eclipse.chart.vmclassstat</module>
+    <module>com.redhat.thermostat.client.feature</module>
     <module>com.redhat.thermostat.eclipse.feature</module>
     <module>com.redhat.thermostat.eclipse.p2-repo</module>
-    <module>com.redhat.thermostat.client.feature</module>
     <module>core-p2-repository</module>
-    <module>test-deps-bundle-wrapping</module>
-    <module>jfreechart-bundle-wrapping</module>
-    <!-- Adds p2 metadata and creates repo for plain (wrapped)
-         Java OSGi bundles so they can be found for building and
-         running tycho tests -->
-    <module>test-deps-p2-repository</module>
-    <module>jfreechart-p2-repository</module>
     <module>composite-repo</module>
   </modules>
 
--- a/launcher/src/main/resources/META-INF/p2.inf	Mon Nov 19 11:35:17 2012 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-# Instructs Eclipse update manager to auto-start this bundle
-instructions.configure = \
-org.eclipse.equinox.p2.touchpoint.eclipse.setStartLevel(startLevel: 4); \
-org.eclipse.equinox.p2.touchpoint.eclipse.markStarted(started: true);
-instructions.unconfigure = \
-org.eclipse.equinox.p2.touchpoint.eclipse.setStartLevel(startLevel: -1); \
-org.eclipse.equinox.p2.touchpoint.eclipse.markStarted(started: false);
--- a/pom.xml	Mon Nov 19 11:35:17 2012 +0100
+++ b/pom.xml	Mon Nov 19 11:40:08 2012 +0100
@@ -128,6 +128,8 @@
     <module>system-backend</module>
     <module>gc</module>
     <module>storage</module>
+    <!-- development related modules -->
+    <module>dev</module>
   </modules>
 
   <build>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/core/src/main/resources/META-INF/p2.inf	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,7 @@
+# Instructs Eclipse update manager to auto-start this bundle
+instructions.configure = \
+org.eclipse.equinox.p2.touchpoint.eclipse.setStartLevel(startLevel: 4); \
+org.eclipse.equinox.p2.touchpoint.eclipse.markStarted(started: true);
+instructions.unconfigure = \
+org.eclipse.equinox.p2.touchpoint.eclipse.setStartLevel(startLevel: -1); \
+org.eclipse.equinox.p2.touchpoint.eclipse.markStarted(started: false);
--- a/web/server/pom.xml	Mon Nov 19 11:35:17 2012 +0100
+++ b/web/server/pom.xml	Mon Nov 19 11:40:08 2012 +0100
@@ -163,6 +163,9 @@
               com.redhat.thermostat.thread.model,
               *
             </Import-Package>
+            <Private-Package>
+              com.redhat.thermostat.web.server.internal
+            </Private-Package>
             <!-- Do not autogenerate uses clauses in Manifests -->
             <_nouses>true</_nouses>
           </instructions>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/server/src/main/java/com/redhat/thermostat/web/server/IpPortPair.java	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.web.server;
+
+public class IpPortPair {
+    private String ip;
+    private int port;
+    
+    public IpPortPair(String ip, int port) {
+        this.ip = ip;
+        this.port = port;
+    }
+
+    public String getIp() {
+        return ip;
+    }
+
+    public int getPort() {
+        return port;
+    }
+}
\ No newline at end of file
--- a/web/server/src/main/java/com/redhat/thermostat/web/server/WebServiceCommand.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/web/server/src/main/java/com/redhat/thermostat/web/server/WebServiceCommand.java	Mon Nov 19 11:40:08 2012 +0100
@@ -37,9 +37,12 @@
 
 package com.redhat.thermostat.web.server;
 
+import java.util.List;
+
 import com.redhat.thermostat.common.cli.CommandContext;
 import com.redhat.thermostat.common.cli.CommandException;
 import com.redhat.thermostat.common.cli.SimpleCommand;
+import com.redhat.thermostat.web.server.internal.IpPortsParser;
 
 public class WebServiceCommand extends SimpleCommand {
 
@@ -57,9 +60,8 @@
     @Override
     public void run(CommandContext ctx) throws CommandException {
         String storageURL = ctx.getArguments().getArgument("storageURL");
-        String port = ctx.getArguments().getArgument("port");
+        serviceLauncher.setIpAddresses(parseIPsPorts(ctx.getArguments().getArgument("bindAddrs")));
         serviceLauncher.setStorageURL(storageURL);
-        serviceLauncher.setPort(Integer.parseInt(port));
         try {
             // this blocks
             serviceLauncher.start();
@@ -87,4 +89,14 @@
         return false;
     }
 
+    private List<IpPortPair> parseIPsPorts(String rawIpsPorts) throws CommandException {
+        IpPortsParser parser = new IpPortsParser(rawIpsPorts);
+        try {
+           parser.parse(); 
+        } catch (IllegalArgumentException e) {
+            throw new CommandException(e);
+        }
+        return parser.getIpsPorts();
+    }
+
 }
--- a/web/server/src/main/java/com/redhat/thermostat/web/server/WebServiceLauncher.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/web/server/src/main/java/com/redhat/thermostat/web/server/WebServiceLauncher.java	Mon Nov 19 11:40:08 2012 +0100
@@ -37,6 +37,8 @@
 
 package com.redhat.thermostat.web.server;
 
+import java.util.List;
+
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.nio.SelectChannelConnector;
@@ -50,7 +52,8 @@
 
     private Server server;
     private String storageURL;
-    private int port = -1;
+    // IP/Port pairs, keyed by IP
+    private List<IpPortPair> ipsPorts;
     
     WebServiceLauncher() {
         server = new Server();
@@ -63,9 +66,14 @@
 
     void start() throws Exception {
         checkConfig();
-        Connector connector = new SelectChannelConnector();
-        connector.setPort(port);
-        server.setConnectors(new Connector[] { connector } );
+        Connector[] connectors = new Connector[ipsPorts.size()];
+        for (int i = 0; i < ipsPorts.size(); i++) {
+            IpPortPair pair = ipsPorts.get(i);
+            connectors[i] = new SelectChannelConnector();
+            connectors[i].setPort(pair.getPort());
+            connectors[i].setHost(pair.getIp());
+        }
+        server.setConnectors( connectors );
         ServletHandler handler = new ServletHandler();
         ServletHolder servletHolder = new ServletHolder("rest-storage-end-point", new WebStorageEndPoint());
         servletHolder.setInitParameter(WebStorageEndPoint.STORAGE_ENDPOINT, storageURL);
@@ -88,19 +96,24 @@
         this.storageURL = storageURL;
     }
 
-    public void setPort(int port) {
-        this.port = port;
+    public void setIpAddresses(List<IpPortPair> ipsPorts) {
+        this.ipsPorts = ipsPorts;
     }
 
     /*
      * StorageURL, port must have been set
      */
     private void checkConfig() throws InvalidConfigurationException {
-        if (port <= 0) {
-            throw new InvalidConfigurationException("Invalid port number: " + port);
-        }
         if (storageURL == null) {
             throw new InvalidConfigurationException("Storage URL must be set");
         }
+        if (ipsPorts == null) {
+            throw new InvalidConfigurationException("IP adresses to bind to must be set");
+        }
+        for (IpPortPair pair: ipsPorts) {
+            if (pair.getPort() <= 0) {
+                throw new InvalidConfigurationException("Invalid port number " + pair.getPort());
+            }
+        }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/server/src/main/java/com/redhat/thermostat/web/server/internal/IpPortsParser.java	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.web.server.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.redhat.thermostat.web.server.IpPortPair;
+
+/**
+ * Parses IP/Port pairs from a raw string of the form:
+ * 
+ * IPv4:
+ *      127.0.0.1:9999,127.0.0.2:8888
+ *      
+ * or
+ * 
+ * IPv6:
+ *      [1fff:0:a88:85a3::ac1f]:8001,[1fff:0:a88:85a3::ac2f]:8001
+ *
+ */
+public class IpPortsParser {
+
+    private final String rawString;
+    private List<IpPortPair> ipPorts;
+    private final IllegalArgumentException formatException; 
+    
+    public IpPortsParser(String parseString) {
+        this.rawString = parseString;
+        this.formatException = new IllegalArgumentException("Invalid format of IP/port argument " + rawString);
+    }
+    
+    public void parse() throws IllegalArgumentException {
+        ipPorts = new ArrayList<>();
+        for (String ipPortPair: rawString.split(",")) {
+            // if we have a '[' in the ip:port pair string we likely have an IPv6
+            int idxRparen = ipPortPair.indexOf(']');
+            int idxLParen = ipPortPair.indexOf('[');
+            if (idxLParen == -1) {
+                // IPv4
+                if (idxRparen != -1 || ipPortPair.indexOf(':') == -1) {
+                   throw formatException; 
+                }
+                String[] ipPort = ipPortPair.split(":");
+                int port = -1;
+                try {
+                    port = Integer.parseInt(ipPort[1]);
+                } catch (NumberFormatException e) {
+                    throw formatException;
+                }
+                ipPorts.add(new IpPortPair(ipPort[0], port));
+            } else {
+                // IPv6
+                if (idxRparen == -1) {
+                    throw formatException;
+                }
+                int port = -1;
+                try {
+                    port = Integer.parseInt(ipPortPair.substring(idxRparen + 2));
+                } catch (NumberFormatException e) {
+                    throw formatException;
+                }
+                ipPorts.add(new IpPortPair(ipPortPair.substring(idxLParen + 1, idxRparen), port));
+            }
+        }
+    }
+    
+    public List<IpPortPair> getIpsPorts() {
+        if (ipPorts == null) {
+            throw new IllegalStateException("Must call parse() before getting map!");
+        }
+        return ipPorts;
+    }
+}
--- a/web/server/src/test/java/com/redhat/thermostat/web/server/WebServiceCommandTest.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/web/server/src/test/java/com/redhat/thermostat/web/server/WebServiceCommandTest.java	Mon Nov 19 11:40:08 2012 +0100
@@ -40,6 +40,9 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.mockito.Matchers.any;
+
+import java.util.List;
 
 import org.junit.After;
 import org.junit.Before;
@@ -75,19 +78,20 @@
         launcher = null;
     }
     
+    @SuppressWarnings("unchecked")
     @Test
     public void verifyLauncherStart() throws Exception {
         SimpleArguments args = new SimpleArguments();
         String storageUrl = "mongodb://127.0.0.1:27518";
         args.addArgument("storageURL", storageUrl);
-        args.addArgument("port", "8082");
+        args.addArgument("bindAddrs", "127.0.0.1:8888,127.0.0.2:9999");
         try {
             cmd.run(cmdCtxFactory.createContext(args));
         } catch (CommandException e) {
             fail("should not throw exception");
         }
-        verify(launcher).setPort(8082);
         verify(launcher).setStorageURL(storageUrl);
+        verify(launcher).setIpAddresses(any(List.class));
         verify(launcher).start();
     }
 }
--- a/web/server/src/test/java/com/redhat/thermostat/web/server/WebServiceLauncherTest.java	Mon Nov 19 11:35:17 2012 +0100
+++ b/web/server/src/test/java/com/redhat/thermostat/web/server/WebServiceLauncherTest.java	Mon Nov 19 11:40:08 2012 +0100
@@ -40,8 +40,12 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import org.eclipse.jetty.server.Server;
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
 
@@ -50,23 +54,32 @@
 public class WebServiceLauncherTest {
     
     private WebServiceLauncher launcher;
+    private List<IpPortPair> dummyIp;
+    
+    @Before
+    public void setUp() {
+        dummyIp = new ArrayList<IpPortPair>();
+        dummyIp.add(new IpPortPair("127.0.0.1", 8889));
+    }
     
     @After
     public void tearDown() {
         launcher = null;
-    }
-    
-    @Test( expected=InvalidConfigurationException.class )
-    public void unsetPortThrowsException() throws Exception {
-        launcher = new WebServiceLauncher();
-        launcher.setStorageURL("mongodb://127.0.0.1:27518");
-        launcher.start();
+        dummyIp = null;
     }
     
     @Test( expected=InvalidConfigurationException.class )
     public void unsetStorageURLThrowsException() throws Exception {
         launcher = new WebServiceLauncher();
-        launcher.setPort(20);
+        launcher.setIpAddresses(dummyIp);
+        launcher.start();
+    }
+    
+    @Test( expected=InvalidConfigurationException.class )
+    public void unsetIpAddressesThrowsException() throws Exception {
+        launcher = new WebServiceLauncher();
+        launcher.setStorageURL("something not null");
+        launcher.setIpAddresses(null);
         launcher.start();
     }
     
@@ -75,14 +88,18 @@
         int excptnsThrown = 0;
         int excptnsExpected = 2;
         launcher = new WebServiceLauncher();
+        List<IpPortPair> ips = new ArrayList<>();
+        ips.add(new IpPortPair("127.0.0.1", -10));
         try {
-            launcher.setPort(-10);
+            launcher.setIpAddresses(ips);
             launcher.start();
         } catch (InvalidConfigurationException e) {
             excptnsThrown++;
         }
+        ips = new ArrayList<>();
+        ips.add(new IpPortPair("127.0.0.1", 0));
         try {
-            launcher.setPort(0);
+            launcher.setIpAddresses(ips);
             launcher.start();
         } catch (InvalidConfigurationException e) {
             excptnsThrown++;
@@ -95,7 +112,7 @@
     public void verifyStartDoesStartServer() throws Exception {
         Server server = mock(Server.class);
         WebServiceLauncher launcher = new WebServiceLauncher(server);
-        launcher.setPort(50);
+        launcher.setIpAddresses(dummyIp);
         launcher.setStorageURL("mongodb://test.example.org/db");
         launcher.start();
         verify(server).start();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/server/src/test/java/com/redhat/thermostat/web/server/internal/IpPortsParserTest.java	Mon Nov 19 11:40:08 2012 +0100
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.web.server.internal;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.web.server.IpPortPair;
+
+public class IpPortsParserTest {
+
+    @Test
+    public void canParsIpV4Pair() throws IllegalArgumentException {
+        IpPortsParser parser = new IpPortsParser(
+                "127.0.0.1:8080,127.0.0.1:9999");
+        parser.parse();
+        List<IpPortPair> ipPorts = parser.getIpsPorts();
+        assertEquals(2, ipPorts.size());
+        assertEquals(8080, (long) ipPorts.get(0).getPort());
+        assertEquals("127.0.0.1", ipPorts.get(0).getIp());
+        assertEquals(9999, (long) ipPorts.get(1).getPort());
+        assertEquals("127.0.0.1", ipPorts.get(1).getIp());
+    }
+
+    @Test
+    public void canParseIpv6Pair() {
+        IpPortsParser parser = new IpPortsParser(
+                "[1fff:0:a88:85a3::ac1f]:8001,[1fff:0:a88:85a3::ac2f]:8001");
+        parser.parse();
+        List<IpPortPair> ipPorts = parser.getIpsPorts();
+        assertEquals(2, ipPorts.size());
+        assertEquals(8001, (long) ipPorts.get(0).getPort());
+        assertEquals("1fff:0:a88:85a3::ac1f", ipPorts.get(0).getIp());
+        assertEquals(8001, (long) ipPorts.get(1).getPort());
+        assertEquals("1fff:0:a88:85a3::ac2f", ipPorts.get(1).getIp());
+    }
+
+    @Test
+    public void failsParsingInvalidString() {
+        IpPortsParser parser = new IpPortsParser(
+                "1fff:0:a88:85a3::ac1f]:8001,[1fff:0:a88:85a3::ac2f]:8001");
+        int expectedExcptns = 3;
+        int exptns = 0;
+        try {
+            parser.parse();
+        } catch (IllegalArgumentException e) {
+            exptns++;
+        }
+        parser = new IpPortsParser("blah,test");
+        try {
+            parser.parse();
+        } catch (IllegalArgumentException e) {
+            exptns++;
+        }
+        parser = new IpPortsParser("127.0.0.1:80,127.0.0.2:bad");
+        try {
+            parser.parse();
+        } catch (IllegalArgumentException e) {
+            exptns++;
+        }
+        assertEquals(expectedExcptns, exptns);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void getMapWithNoParseThrowsException() {
+        IpPortsParser parser = new IpPortsParser("blah");
+        parser.getIpsPorts();
+    }
+}