changeset 1096:e9abee8e0f3c

Merge
author Elliott Baron <ebaron@redhat.com>
date Fri, 17 May 2013 14:22:28 -0400
parents 8b19c4771db1 (current diff) 0d23a5a62b1a (diff)
children e31208c2d0c8
files
diffstat 43 files changed, 3469 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/agent/core/src/main/java/com/redhat/thermostat/utils/management/MXBeanConnection.java	Fri May 17 14:08:49 2013 -0400
+++ b/agent/core/src/main/java/com/redhat/thermostat/utils/management/MXBeanConnection.java	Fri May 17 14:22:28 2013 -0400
@@ -36,10 +36,13 @@
 
 package com.redhat.thermostat.utils.management;
 
+import javax.management.MBeanServerConnection;
 import javax.management.MalformedObjectNameException;
 
 public interface MXBeanConnection {
 
     <E> E createProxy(String name, Class<? extends E> proxyClass) throws MalformedObjectNameException;
 
+    MBeanServerConnection get();
+
 }
--- a/agent/core/src/main/java/com/redhat/thermostat/utils/management/internal/MXBeanConnectionImpl.java	Fri May 17 14:08:49 2013 -0400
+++ b/agent/core/src/main/java/com/redhat/thermostat/utils/management/internal/MXBeanConnectionImpl.java	Fri May 17 14:22:28 2013 -0400
@@ -61,6 +61,11 @@
         return JMX.newMXBeanProxy(mbsc, objectName, proxyClass);
     }
     
+    @Override
+    public MBeanServerConnection get() {
+        return mbsc;
+    }
+
     void close() throws IOException {
         connection.close();
     }
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/IconResource.java	Fri May 17 14:08:49 2013 -0400
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/IconResource.java	Fri May 17 14:22:28 2013 -0400
@@ -68,6 +68,8 @@
 
     public static final IconResource SEARCH = new IconResource(ICON_PREFIX + "16x16/actions/search.png");
 
+    public static final IconResource RECORD = new IconResource(ICON_PREFIX + "16x16/actions/media-record.png");
+
     private final String path;
 
     private IconResource(String descriptor) {
--- a/distribution/assembly/all-plugin-assembly.xml	Fri May 17 14:08:49 2013 -0400
+++ b/distribution/assembly/all-plugin-assembly.xml	Fri May 17 14:22:28 2013 -0400
@@ -58,6 +58,7 @@
         <include>com.redhat.thermostat:thermostat-vm-cpu-distribution</include>
         <include>com.redhat.thermostat:thermostat-vm-gc-distribution</include>
         <include>com.redhat.thermostat:thermostat-vm-heap-analysis-distribution</include>
+        <include>com.redhat.thermostat:thermostat-vm-jmx-distribution</include>
         <include>com.redhat.thermostat:thermostat-vm-memory-distribution</include>
         <include>com.redhat.thermostat:thermostat-vm-overview-distribution</include>
       </includes>
--- a/distribution/pom.xml	Fri May 17 14:08:49 2013 -0400
+++ b/distribution/pom.xml	Fri May 17 14:22:28 2013 -0400
@@ -513,6 +513,12 @@
     </dependency>
     <dependency>
       <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-jmx-distribution</artifactId>
+      <version>${project.version}</version>
+      <type>zip</type>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
       <artifactId>thermostat-vm-memory-distribution</artifactId>
       <version>${project.version}</version>
       <type>zip</type>
--- a/pom.xml	Fri May 17 14:08:49 2013 -0400
+++ b/pom.xml	Fri May 17 14:22:28 2013 -0400
@@ -147,6 +147,7 @@
     <module>vm-classstat</module>
     <module>vm-memory</module>
     <module>vm-heap-analysis</module>
+    <module>vm-jmx</module>
     <module>numa</module>
     <!-- development related modules -->
     <module>integration-tests</module>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/agent/pom.xml	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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>
+    <artifactId>thermostat-vm-jmx</artifactId>
+    <groupId>com.redhat.thermostat</groupId>
+    <version>0.8.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>thermostat-vm-jmx-agent</artifactId>
+  <packaging>bundle</packaging>
+  <name>Thermostat VM JMX Agent plugin</name>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
+            <Bundle-SymbolicName>com.redhat.thermostat.vm.jmx.agent</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.vm.jmx.agent.internal.Activator</Bundle-Activator>
+            <Export-Package />
+            <Private-Package>
+              com.redhat.thermostat.vm.jmx.agent.internal
+            </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-test</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.compendium</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-agent-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-agent-command</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-jmx-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/agent/src/main/java/com/redhat/thermostat/vm/jmx/agent/internal/Activator.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.agent.internal;
+
+import java.util.Map;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+import com.redhat.thermostat.agent.command.ReceiverRegistry;
+import com.redhat.thermostat.backend.Backend;
+import com.redhat.thermostat.common.MultipleServiceTracker;
+import com.redhat.thermostat.common.Version;
+import com.redhat.thermostat.utils.management.MXBeanConnectionPool;
+import com.redhat.thermostat.vm.jmx.common.JmxNotificationDAO;
+
+public class Activator implements BundleActivator {
+
+    private ServiceRegistration registration;
+    private JmxBackend jmxBackend;
+    private MultipleServiceTracker tracker;
+
+    @Override
+    public void start(final BundleContext context) throws Exception {
+
+        Class<?>[] deps = new Class<?>[] { JmxNotificationDAO.class, MXBeanConnectionPool.class };
+        tracker = new MultipleServiceTracker(context, deps, new MultipleServiceTracker.Action() {
+
+            @Override
+            public void dependenciesAvailable(Map<String, Object> services) {
+                MXBeanConnectionPool pool = (MXBeanConnectionPool) services.get(MXBeanConnectionPool.class.getName());
+                JmxNotificationDAO dao = (JmxNotificationDAO) services.get(JmxNotificationDAO.class.getName());
+                Version version = new Version(context.getBundle());
+                ReceiverRegistry registry = new ReceiverRegistry(context);
+                JmxRequestListener receiver = new JmxRequestListener();
+                jmxBackend = new JmxBackend(version, registry, dao, pool, receiver);
+                receiver.setBackend(jmxBackend);
+                registration = context.registerService(Backend.class, jmxBackend, null);
+            }
+
+            @Override
+            public void dependenciesUnavailable() {
+                registration.unregister();
+                registration = null;
+                if (jmxBackend.isActive()) {
+                    jmxBackend.deactivate();
+                }
+            }
+        });
+
+        tracker.open();
+
+    }
+
+    /** For testing only */
+    JmxBackend getBackend() {
+        return jmxBackend;
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        tracker.close();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/agent/src/main/java/com/redhat/thermostat/vm/jmx/agent/internal/JmxBackend.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.agent.internal;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.management.InstanceNotFoundException;
+import javax.management.IntrospectionException;
+import javax.management.MBeanServerConnection;
+import javax.management.MBeanServerDelegate;
+import javax.management.MBeanServerNotification;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+
+import com.redhat.thermostat.agent.command.ReceiverRegistry;
+import com.redhat.thermostat.agent.command.RequestReceiver;
+import com.redhat.thermostat.backend.BaseBackend;
+import com.redhat.thermostat.common.Version;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.utils.management.MXBeanConnection;
+import com.redhat.thermostat.utils.management.MXBeanConnectionPool;
+import com.redhat.thermostat.vm.jmx.common.JmxNotification;
+import com.redhat.thermostat.vm.jmx.common.JmxNotificationDAO;
+
+public class JmxBackend extends BaseBackend {
+
+    private static final Logger logger = LoggingUtils.getLogger(JmxBackend.class);
+
+    private final ReceiverRegistry registry;
+    private final RequestReceiver receiver;
+    private final JmxNotificationDAO dao;
+    private final MXBeanConnectionPool pool;
+
+    private final NotificationListener registrationNotificationListener;
+    private final NotificationListener notificationWriter;
+
+    private final Map<Integer, MXBeanConnection> connections = new HashMap<>();
+
+    private boolean isActive = false;
+
+    public JmxBackend(Version version, ReceiverRegistry registry, JmxNotificationDAO dao, MXBeanConnectionPool pool, RequestReceiver receiver) {
+        super("VM JMX Backend", "gathers JMX information using JMX", "Red Hat, Inc.", version.getVersionNumber());
+
+        this.registry = registry;
+        this.pool = pool;
+        this.dao = dao;
+
+        this.registrationNotificationListener = new RegistrationNotificationListener();
+        this.notificationWriter = new NotificationWriter();
+
+        this.receiver = receiver;
+    }
+
+    @Override
+    public int getOrderValue() {
+        return ORDER_USER_GROUP + 99;
+    }
+
+    @Override
+    public boolean activate() {
+        if (isActive) {
+            return true;
+        }
+
+        registry.registerReceiver(receiver);
+
+        isActive = true;
+        return isActive;
+    }
+
+    @Override
+    public boolean deactivate() {
+        if (!isActive) {
+            return true;
+        }
+
+        registry.unregisterReceivers();
+
+        isActive = false;
+        return true;
+    }
+
+    @Override
+    public boolean isActive() {
+        return isActive;
+    }
+
+    public void enableNotificationsFor(String vmId) {
+        int pid = Integer.valueOf(vmId);
+        try {
+            MXBeanConnection connection = pool.acquire(pid);
+            connections.put(pid, connection);
+            MBeanServerConnection server = connection.get();
+            Set<ObjectName> names = server.queryNames(null, null);
+            for (ObjectName name : names) {
+                if (name.equals(MBeanServerDelegate.DELEGATE_NAME)) {
+                    server.addNotificationListener(MBeanServerDelegate.DELEGATE_NAME, this.registrationNotificationListener, null, pid);
+                } else {
+                    addNotificationListenerToMBean(pid, server, name);
+                }
+            }
+        } catch (Exception e) {
+            logger.log(Level.WARNING, "Unable to connect to the mx bean connector", e);
+        }
+    }
+
+    public void disableNotificationsFor(String vmId) {
+        int pid = Integer.valueOf(vmId);
+
+        MXBeanConnection connection = connections.get(pid);
+        try {
+            pool.release(pid, connection);
+        } catch (Exception e) {
+            logger.warning("Unable to release mx bean connection");
+        }
+    }
+
+    private class RegistrationNotificationListener implements NotificationListener {
+
+        @Override
+        public void handleNotification(Notification notification, Object handback) {
+            Integer pid = (Integer) handback;
+            MBeanServerConnection server = connections.get(pid).get();
+            MBeanServerNotification serverNotification = (MBeanServerNotification) notification;
+            ObjectName name = serverNotification.getMBeanName();
+
+            try {
+                if (MBeanServerNotification.REGISTRATION_NOTIFICATION.equals(serverNotification.getType())) {
+                    logger.fine("MBean Registered: " + name);
+                    addNotificationListenerToMBean(pid, server, name);
+                } else if (MBeanServerNotification.UNREGISTRATION_NOTIFICATION.equals(serverNotification.getType())) {
+                    logger.fine("MBean Unregistered: " + name);
+                    // we should remove the listener, but the object is not
+                    // around to emit notifications
+                }
+            } catch (IOException | InstanceNotFoundException | IntrospectionException | ReflectionException e) {
+                logger.log(Level.WARNING, "exception while handling MBeanServerNotification", e);
+            }
+        }
+
+    }
+
+    // Writes the notification to storage
+    private class NotificationWriter implements NotificationListener {
+
+        @Override
+        public void handleNotification(Notification notification, Object handback) {
+            int pid = (Integer) handback;
+
+            JmxNotification data = new JmxNotification();
+            data.setVmId(pid);
+            data.setImportance("unknown");
+            data.setTimeStamp(notification.getTimeStamp());
+            data.setSourceBackend(JmxBackend.class.getName());
+            data.setSourceDetails("dunno");
+            data.setContents(notification.toString());
+            dao.addNotification(data);
+
+        }
+    }
+
+    private void addNotificationListenerToMBean(int pid, MBeanServerConnection server, ObjectName name)
+            throws InstanceNotFoundException, IntrospectionException, ReflectionException, IOException {
+        if (server.getMBeanInfo(name).getNotifications().length > 0) {
+            server.addNotificationListener(name, JmxBackend.this.notificationWriter, null, pid);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/agent/src/main/java/com/redhat/thermostat/vm/jmx/agent/internal/JmxRequestListener.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.agent.internal;
+
+import com.redhat.thermostat.agent.command.RequestReceiver;
+import com.redhat.thermostat.common.command.Request;
+import com.redhat.thermostat.common.command.Response;
+import com.redhat.thermostat.common.command.Response.ResponseType;
+import com.redhat.thermostat.vm.jmx.common.JmxCommand;
+
+public class JmxRequestListener implements RequestReceiver {
+
+    private JmxBackend backend;
+
+    public void setBackend(JmxBackend backend) {
+        this.backend = backend;
+    }
+
+    @Override
+    public Response receive(Request request) {
+        Response response = new Response(ResponseType.OK);
+        String vmId = request.getParameter(JmxCommand.VM_ID);
+        String command = request.getParameter(JmxCommand.class.getName());
+
+        switch (JmxCommand.valueOf(command)) {
+        case DISABLE_JMX_NOTIFICATIONS:
+            backend.disableNotificationsFor(vmId);
+            break;
+        case ENABLE_JMX_NOTIFICATIONS:
+            backend.enableNotificationsFor(vmId);
+            break;
+        }
+
+        return response;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/agent/src/test/java/com/redhat/thermostat/vm/jmx/agent/internal/ActivatorTest.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.agent.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Version;
+
+import com.redhat.thermostat.backend.Backend;
+import com.redhat.thermostat.testutils.StubBundleContext;
+import com.redhat.thermostat.utils.management.MXBeanConnectionPool;
+import com.redhat.thermostat.vm.jmx.agent.internal.Activator;
+import com.redhat.thermostat.vm.jmx.agent.internal.JmxBackend;
+import com.redhat.thermostat.vm.jmx.common.JmxNotificationDAO;
+
+public class ActivatorTest {
+
+    @Test
+    public void verifyActviatorRegistersBackend() throws Exception {
+        StubBundleContext bundleContext = new StubBundleContext();
+
+        Bundle bundle = mock(Bundle.class);
+        Version version = new Version(1, 1, 1);
+        when(bundle.getVersion()).thenReturn(version);
+        bundleContext.setBundle(bundle);
+
+        bundleContext.registerService(JmxNotificationDAO.class, mock(JmxNotificationDAO.class), null);
+        bundleContext.registerService(MXBeanConnectionPool.class, mock(MXBeanConnectionPool.class), null);
+
+        Activator activator = new Activator();
+
+        activator.start(bundleContext);
+
+        assertTrue(bundleContext.isServiceRegistered(Backend.class.getName(), JmxBackend.class));
+        assertEquals(3, bundleContext.getAllServices().size());
+
+        // thermostat will activate the backend; simulate that
+        activator.getBackend().activate();
+
+        activator.stop(bundleContext);
+
+        assertEquals(2, bundleContext.getAllServices().size());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/agent/src/test/java/com/redhat/thermostat/vm/jmx/agent/internal/JmxBackendTest.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.agent.internal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isA;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanServerConnection;
+import javax.management.MBeanServerDelegate;
+import javax.management.MBeanServerNotification;
+import javax.management.Notification;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.agent.command.ReceiverRegistry;
+import com.redhat.thermostat.agent.command.RequestReceiver;
+import com.redhat.thermostat.common.Ordered;
+import com.redhat.thermostat.common.Version;
+import com.redhat.thermostat.utils.management.MXBeanConnection;
+import com.redhat.thermostat.utils.management.MXBeanConnectionPool;
+import com.redhat.thermostat.vm.jmx.common.JmxNotification;
+import com.redhat.thermostat.vm.jmx.common.JmxNotificationDAO;
+
+public class JmxBackendTest {
+
+    private Version version;
+    private ReceiverRegistry registry;
+    private MXBeanConnectionPool pool;
+    private JmxNotificationDAO dao;
+    private JmxBackend backend;
+    private RequestReceiver receiver;
+
+    @Before
+    public void setUp() {
+        version = mock(Version.class);
+        when(version.getVersionNumber()).thenReturn("foo");
+
+        registry = mock(ReceiverRegistry.class);
+        receiver = mock(RequestReceiver.class);
+        dao = mock(JmxNotificationDAO.class);
+        pool = mock(MXBeanConnectionPool.class);
+
+        backend = new JmxBackend(version, registry, dao, pool, receiver);
+    }
+
+    @Test
+    public void testOrderValue() {
+        assertTrue(backend.getOrderValue() > Ordered.ORDER_USER_GROUP);
+    }
+
+    @Test
+    public void testActivation() {
+        assertFalse(backend.isActive());
+        assertTrue(backend.activate());
+        assertTrue(backend.isActive());
+        assertTrue(backend.activate());
+        assertTrue(backend.isActive());
+    }
+
+    @Test
+    public void testDeactivation() {
+        assertTrue(backend.activate());
+
+        assertTrue(backend.isActive());
+
+        assertTrue(backend.deactivate());
+
+        assertFalse(backend.isActive());
+    }
+
+    @Test
+    public void testDectivateTwice() {
+        assertTrue(backend.activate());
+        assertTrue(backend.isActive());
+
+        assertTrue(backend.deactivate());
+        assertFalse(backend.isActive());
+        assertTrue(backend.deactivate());
+        assertFalse(backend.isActive());
+    }
+
+    @Test
+    public void testNotificationsEnabled() throws Exception {
+        ObjectName name1 = mock(ObjectName.class);
+        Set<ObjectName> names = new HashSet<ObjectName>();
+        names.add(name1);
+
+        MBeanInfo info1 = mock(MBeanInfo.class);
+        when(info1.getNotifications()).thenReturn(new MBeanNotificationInfo[10]);
+
+        MBeanServerConnection actual = mock(MBeanServerConnection.class);
+        when(actual.queryNames(null, null)).thenReturn(names);
+        when(actual.getMBeanInfo(name1)).thenReturn(info1);
+
+        MXBeanConnection connection = mock(MXBeanConnection.class);
+        when(connection.get()).thenReturn(actual);
+        when(pool.acquire(0)).thenReturn(connection);
+
+        backend.enableNotificationsFor("0");
+
+        verify(actual).addNotificationListener(eq(name1), any(NotificationListener.class), eq((NotificationFilter) null), any());
+    }
+
+    @Test
+    public void testJmxNotificationAddedToStorage() throws Exception {
+        ObjectName name1 = mock(ObjectName.class);
+        Set<ObjectName> names = new HashSet<ObjectName>();
+        names.add(name1);
+
+        MBeanInfo info1 = mock(MBeanInfo.class);
+        when(info1.getNotifications()).thenReturn(new MBeanNotificationInfo[10]);
+
+        MBeanServerConnection actual = mock(MBeanServerConnection.class);
+        when(actual.queryNames(null, null)).thenReturn(names);
+        when(actual.getMBeanInfo(name1)).thenReturn(info1);
+
+        MXBeanConnection connection = mock(MXBeanConnection.class);
+        when(connection.get()).thenReturn(actual);
+        when(pool.acquire(0)).thenReturn(connection);
+
+        backend.enableNotificationsFor("0");
+
+        ArgumentCaptor<NotificationListener> listenerCaptor = ArgumentCaptor.forClass(NotificationListener.class);
+        ArgumentCaptor<Object> handbackCaptor = ArgumentCaptor.forClass(Object.class);
+
+        verify(actual).addNotificationListener(eq(name1), listenerCaptor.capture(), eq((NotificationFilter) null), handbackCaptor.capture());
+
+        NotificationListener listener = listenerCaptor.getValue();
+
+        Notification notification = mock(Notification.class);
+        when(notification.toString()).thenReturn("testing");
+
+        listener.handleNotification(notification, handbackCaptor.getValue());
+
+        verify(dao).addNotification(isA(JmxNotification.class));
+    }
+
+    @Test
+    public void testMBeanRegisteredNotification() throws Exception {
+        ObjectName serverBean = MBeanServerDelegate.DELEGATE_NAME;
+        Set<ObjectName> names = new HashSet<ObjectName>();
+        names.add(serverBean);
+
+        MBeanInfo serverBeanInfo = mock(MBeanInfo.class);
+        when(serverBeanInfo.getNotifications()).thenReturn(new MBeanNotificationInfo[10]);
+
+        ObjectName newBeanName = mock(ObjectName.class);
+        MBeanInfo newBeanInfo = mock(MBeanInfo.class);
+        when(newBeanInfo.getNotifications()).thenReturn(new MBeanNotificationInfo[1]);
+
+        MBeanServerNotification newMBeanNotification = mock(MBeanServerNotification.class);
+        when(newMBeanNotification.getType()).thenReturn(MBeanServerNotification.REGISTRATION_NOTIFICATION);
+        when(newMBeanNotification.getMBeanName()).thenReturn(newBeanName);
+
+        MBeanServerConnection actual = mock(MBeanServerConnection.class);
+        when(actual.queryNames(null, null)).thenReturn(names);
+        when(actual.getMBeanInfo(serverBean)).thenReturn(serverBeanInfo);
+        when(actual.getMBeanInfo(newBeanName)).thenReturn(newBeanInfo);
+
+        MXBeanConnection connection = mock(MXBeanConnection.class);
+        when(connection.get()).thenReturn(actual);
+        when(pool.acquire(0)).thenReturn(connection);
+
+        backend.enableNotificationsFor("0");
+
+        ArgumentCaptor<NotificationListener> listenerCaptor = ArgumentCaptor.forClass(NotificationListener.class);
+        ArgumentCaptor<Object> handbackCaptor = ArgumentCaptor.forClass(Object.class);
+
+        verify(actual).addNotificationListener(eq(serverBean), listenerCaptor.capture(), eq((NotificationFilter) null), handbackCaptor.capture());
+
+        NotificationListener listener = listenerCaptor.getValue();
+
+        listener.handleNotification(newMBeanNotification, handbackCaptor.getValue());
+
+        verify(actual).addNotificationListener(eq(newBeanName), isA(NotificationListener.class), eq((NotificationFilter) null), eq(handbackCaptor.getValue()));
+    }
+
+    @Test
+    public void testMBeanUnregisteredNotification() throws Exception {
+        ObjectName serverBean = MBeanServerDelegate.DELEGATE_NAME;
+        Set<ObjectName> names = new HashSet<ObjectName>();
+        names.add(serverBean);
+
+        MBeanInfo serverBeanInfo = mock(MBeanInfo.class);
+        when(serverBeanInfo.getNotifications()).thenReturn(new MBeanNotificationInfo[10]);
+
+        MBeanServerNotification mBeanRemovedNotification = mock(MBeanServerNotification.class);
+        when(mBeanRemovedNotification.getType()).thenReturn(MBeanServerNotification.UNREGISTRATION_NOTIFICATION);
+
+        MBeanServerConnection actual = mock(MBeanServerConnection.class);
+        when(actual.queryNames(null, null)).thenReturn(names);
+        when(actual.getMBeanInfo(serverBean)).thenReturn(serverBeanInfo);
+
+        MXBeanConnection connection = mock(MXBeanConnection.class);
+        when(connection.get()).thenReturn(actual);
+        when(pool.acquire(0)).thenReturn(connection);
+
+        backend.enableNotificationsFor("0");
+
+        ArgumentCaptor<NotificationListener> listenerCaptor = ArgumentCaptor.forClass(NotificationListener.class);
+        ArgumentCaptor<Object> handbackCaptor = ArgumentCaptor.forClass(Object.class);
+
+        verify(actual).addNotificationListener(eq(serverBean), listenerCaptor.capture(), eq((NotificationFilter) null), handbackCaptor.capture());
+
+        NotificationListener listener = listenerCaptor.getValue();
+
+        verify(actual).queryNames(null, null);
+        verify(actual).addNotificationListener(eq(MBeanServerDelegate.DELEGATE_NAME), isA(NotificationListener.class), eq((NotificationFilter) null), isA(Integer.class));
+
+        listener.handleNotification(mBeanRemovedNotification, handbackCaptor.getValue());
+
+        verifyNoMoreInteractions(actual);
+    }
+
+    @Test
+    public void testNotificationsDisabled() throws Exception {
+        Set<ObjectName> names = new HashSet<ObjectName>();
+
+        MBeanServerConnection actual = mock(MBeanServerConnection.class);
+        when(actual.queryNames(null, null)).thenReturn(names);
+
+        MXBeanConnection connection = mock(MXBeanConnection.class);
+        when(connection.get()).thenReturn(actual);
+        when(pool.acquire(0)).thenReturn(connection);
+
+        backend.enableNotificationsFor("0");
+
+        backend.disableNotificationsFor("0");
+
+        verify(pool).release(0, connection);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/agent/src/test/java/com/redhat/thermostat/vm/jmx/agent/internal/JmxRequestListenerTest.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.agent.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import java.net.InetSocketAddress;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.common.command.Request;
+import com.redhat.thermostat.common.command.Request.RequestType;
+import com.redhat.thermostat.common.command.Response.ResponseType;
+import com.redhat.thermostat.common.command.Response;
+import com.redhat.thermostat.vm.jmx.common.JmxCommand;
+
+public class JmxRequestListenerTest {
+
+    private static final String HOST = "example.org";
+    private static final int PORT = 0;
+
+    private JmxRequestListener requestListener;
+    private JmxBackend backend;
+
+    @Before
+    public void setUp() {
+        backend = mock(JmxBackend.class);
+
+        requestListener = new JmxRequestListener();
+        requestListener.setBackend(backend);
+    }
+
+    @Test
+    public void testEnableNotifications() {
+        Request req = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress(HOST, PORT));
+        req.setParameter(JmxCommand.class.getName(), JmxCommand.ENABLE_JMX_NOTIFICATIONS.name());
+        req.setParameter(JmxCommand.VM_ID, "0");
+
+        Response result = requestListener.receive(req);
+
+        verify(backend).enableNotificationsFor("0");
+
+        assertEquals(ResponseType.OK, result.getType());
+    }
+
+    @Test
+    public void testDisableNotifications() {
+        Request req = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress(HOST, PORT));
+        req.setParameter(JmxCommand.class.getName(), JmxCommand.DISABLE_JMX_NOTIFICATIONS.name());
+        req.setParameter(JmxCommand.VM_ID, "0");
+
+        Response result = requestListener.receive(req);
+
+        verify(backend).disableNotificationsFor("0");
+
+        assertEquals(ResponseType.OK, result.getType());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/client-core/pom.xml	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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>
+    <artifactId>thermostat-vm-jmx</artifactId>
+    <groupId>com.redhat.thermostat</groupId>
+    <version>0.8.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>thermostat-vm-jmx-client-core</artifactId>
+  <packaging>bundle</packaging>
+  <name>Thermostat VM JMX Client plugin</name>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
+            <Bundle-SymbolicName>com.redhat.thermostat.vm.jmx.client.core</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.vm.jmx.client.core.internal.Activator</Bundle-Activator>
+            <Export-Package>
+              com.redhat.thermostat.vm.jmx.client.core,
+            </Export-Package>
+            <Private-Package>
+              com.redhat.thermostat.vm.jmx.client.core.internal,
+            </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-test</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.compendium</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-client-command</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-client-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-jmx-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/JmxNotificationsView.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.client.core;
+
+import com.redhat.thermostat.client.core.views.BasicView;
+import com.redhat.thermostat.client.core.views.UIComponent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.vm.jmx.common.JmxNotification;
+
+public abstract class JmxNotificationsView extends BasicView implements UIComponent {
+
+    public enum NotificationAction {
+        TOGGLE_NOTIFICATIONS,
+    }
+
+    public abstract void addNotificationActionListener(ActionListener<NotificationAction> listener);
+    public abstract void removeNotificationActionListener(ActionListener<NotificationAction> listener);
+
+    public abstract void setNotificationsEnabled(boolean enabled);
+    public abstract void clearNotifications();
+    public abstract void addNotification(JmxNotification data);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/JmxNotificationsViewProvider.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.client.core;
+
+import com.redhat.thermostat.client.core.views.ViewProvider;
+
+public interface JmxNotificationsViewProvider extends ViewProvider {
+
+    @Override
+    public JmxNotificationsView createView();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/JmxNotificationsViewService.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.client.core;
+
+import com.redhat.thermostat.client.core.InformationService;
+import com.redhat.thermostat.storage.core.VmRef;
+
+public interface JmxNotificationsViewService extends InformationService<VmRef> {
+
+    public static final String SERVICE_ID = "com.redhat.thermostat.vm.jmx.notifications";
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/internal/Activator.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.client.core.internal;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+import com.redhat.thermostat.client.command.RequestQueue;
+import com.redhat.thermostat.client.core.InformationService;
+import com.redhat.thermostat.common.ApplicationService;
+import com.redhat.thermostat.common.Constants;
+import com.redhat.thermostat.common.MultipleServiceTracker;
+import com.redhat.thermostat.common.MultipleServiceTracker.Action;
+import com.redhat.thermostat.common.TimerFactory;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.vm.jmx.client.core.JmxNotificationsViewProvider;
+import com.redhat.thermostat.vm.jmx.common.JmxNotificationDAO;
+
+public class Activator implements BundleActivator {
+
+    private MultipleServiceTracker depsTracker;
+    protected ServiceRegistration registration;
+
+    @Override
+    public void start(final BundleContext context) throws Exception {
+
+        final Class<?>[] deps = new Class<?>[] {
+                AgentInfoDAO.class,
+                JmxNotificationDAO.class,
+                ApplicationService.class,
+                RequestQueue.class,
+                JmxNotificationsViewProvider.class,
+        };
+
+        depsTracker = new MultipleServiceTracker(context, deps, new Action() {
+            @Override
+            public void dependenciesAvailable(Map<String, Object> services) {
+                AgentInfoDAO agentDao = (AgentInfoDAO) services.get(AgentInfoDAO.class.getName());
+                JmxNotificationDAO notificationDao = (JmxNotificationDAO) services.get(JmxNotificationDAO.class.getName());
+                JmxNotificationsViewProvider viewProvider = (JmxNotificationsViewProvider) services.get(JmxNotificationsViewProvider.class.getName());
+                TimerFactory tf = ((ApplicationService) services.get(ApplicationService.class.getName())).getTimerFactory();
+                RequestQueue queue = (RequestQueue) services.get(RequestQueue.class.getName());
+
+                JmxNotificationsViewServiceImpl notificationsView = new JmxNotificationsViewServiceImpl(agentDao, notificationDao, queue, tf, viewProvider);
+
+                Dictionary props = new Hashtable();
+                props.put(Constants.GENERIC_SERVICE_CLASSNAME, VmRef.class.getName());
+
+                registration = context.registerService(InformationService.class, notificationsView, props);
+            }
+
+            @Override
+            public void dependenciesUnavailable() {
+                registration.unregister();
+            }
+
+        });
+        depsTracker.open();
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+
+        depsTracker.close();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewController.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.client.core.internal;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.redhat.thermostat.client.command.RequestQueue;
+import com.redhat.thermostat.client.core.controllers.InformationServiceController;
+import com.redhat.thermostat.client.core.views.BasicView;
+import com.redhat.thermostat.client.core.views.BasicView.Action;
+import com.redhat.thermostat.client.core.views.UIComponent;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.TimerFactory;
+import com.redhat.thermostat.common.command.Request;
+import com.redhat.thermostat.common.command.RequestResponseListener;
+import com.redhat.thermostat.common.command.Response;
+import com.redhat.thermostat.common.command.Response.ResponseType;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.vm.jmx.client.core.JmxNotificationsView;
+import com.redhat.thermostat.vm.jmx.client.core.JmxNotificationsViewProvider;
+import com.redhat.thermostat.vm.jmx.client.core.JmxNotificationsView.NotificationAction;
+import com.redhat.thermostat.vm.jmx.common.JmxNotificationDAO;
+import com.redhat.thermostat.vm.jmx.common.JmxNotification;
+
+public class JmxNotificationsViewController implements InformationServiceController<VmRef> {
+
+    private final JmxNotificationsView view;
+    private final Timer timer;
+    private final JmxNotificationDAO dao;
+    private final AgentInfoDAO agentDAO;
+    private final VmRef vm;
+
+    private final AtomicBoolean toEnable = new AtomicBoolean(true);;
+
+    public JmxNotificationsViewController(AgentInfoDAO agent, JmxNotificationDAO notification,
+            TimerFactory timerFactory, final RequestQueue queue,
+            JmxNotificationsViewProvider viewProvider,
+            VmRef vmId) {
+        this.dao = notification;
+        this.agentDAO = agent;
+        this.view = viewProvider.createView();
+        this.timer = timerFactory.createTimer();
+        this.vm = vmId;
+
+        initializeTimer();
+
+        view.addActionListener(new ActionListener<BasicView.Action>() {
+
+            @Override
+            public void actionPerformed(ActionEvent<Action> actionEvent) {
+                switch (actionEvent.getActionId()) {
+                case HIDDEN:
+                    stopUpdatingView();
+                    break;
+                case VISIBLE:
+                    startUpdatingView();
+                    break;
+                }
+            }
+        });
+
+        view.addNotificationActionListener(new ActionListener<JmxNotificationsView.NotificationAction>() {
+            @Override
+            public void actionPerformed(ActionEvent<NotificationAction> actionEvent) {
+                if (actionEvent.getActionId() == NotificationAction.TOGGLE_NOTIFICATIONS) {
+                    new JmxToggleNotificationRequest(queue).sendEnableNotificationsRequestToAgent(vm, agentDAO, toEnable.get(),
+                            new RequestResponseListener() {
+                                @Override
+                                public void fireComplete(Request request, Response response) {
+                                    if (response.getType() == ResponseType.OK) {
+                                        view.setNotificationsEnabled(toEnable.get());
+                                        toEnable.set(!toEnable.get());
+                                    }
+                                }
+                            });
+                }
+            }
+        });
+
+    }
+
+    private void initializeTimer() {
+        timer.setTimeUnit(TimeUnit.SECONDS);
+        timer.setInitialDelay(0);
+        timer.setDelay(5);
+        timer.setSchedulingType(Timer.SchedulingType.FIXED_RATE);
+        timer.setAction(new Runnable() {
+
+            private long lastTimeStamp = Long.MIN_VALUE;
+
+            @Override
+            public void run() {
+                List<JmxNotification> notifications = dao.getNotifications(vm, lastTimeStamp);
+                for (JmxNotification notification : notifications) {
+                    lastTimeStamp = Math.max(lastTimeStamp, notification.getTimeStamp());
+                    view.addNotification(notification);
+                }
+            }
+        });
+
+    }
+
+    private void startUpdatingView() {
+        timer.start();
+    }
+
+    private void stopUpdatingView() {
+        timer.stop();
+    }
+
+    @Override
+    public UIComponent getView() {
+        return view;
+    }
+
+    @Override
+    public String getLocalizedName() {
+        return "Notifications";
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewServiceImpl.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.client.core.internal;
+
+import com.redhat.thermostat.client.command.RequestQueue;
+import com.redhat.thermostat.client.core.Filter;
+import com.redhat.thermostat.client.core.NameMatchingRefFilter;
+import com.redhat.thermostat.client.core.controllers.InformationServiceController;
+import com.redhat.thermostat.common.Ordered;
+import com.redhat.thermostat.common.TimerFactory;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.vm.jmx.client.core.JmxNotificationsViewProvider;
+import com.redhat.thermostat.vm.jmx.client.core.JmxNotificationsViewService;
+import com.redhat.thermostat.vm.jmx.common.JmxNotificationDAO;
+
+public class JmxNotificationsViewServiceImpl implements JmxNotificationsViewService {
+
+    private final Filter<VmRef> FILTER = new NameMatchingRefFilter<>();
+
+    private final JmxNotificationsViewProvider viewProvider;
+    private final JmxNotificationDAO notificationDao;
+    private final AgentInfoDAO agentDao;
+    private final RequestQueue requestQueue;
+    private final TimerFactory timerFactory;
+
+    public JmxNotificationsViewServiceImpl(AgentInfoDAO agentDao, JmxNotificationDAO notificationDao,
+            RequestQueue requestQueue, TimerFactory timerFactory, JmxNotificationsViewProvider viewProvider) {
+        this.agentDao = agentDao;
+        this.notificationDao = notificationDao;
+        this.timerFactory = timerFactory;
+        this.requestQueue = requestQueue;
+        this.viewProvider = viewProvider;
+    }
+
+    @Override
+    public Filter<VmRef> getFilter() {
+        return FILTER;
+    }
+
+    @Override
+    public InformationServiceController<VmRef> getInformationServiceController(VmRef ref) {
+        return new JmxNotificationsViewController(agentDao, notificationDao, timerFactory, requestQueue, viewProvider, ref);
+    }
+
+    @Override
+    public int getOrderValue() {
+        return Ordered.ORDER_USER_GROUP + 101;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxToggleNotificationRequest.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.client.core.internal;
+
+import java.net.InetSocketAddress;
+
+import com.redhat.thermostat.client.command.RequestQueue;
+import com.redhat.thermostat.common.command.Request;
+import com.redhat.thermostat.common.command.RequestResponseListener;
+import com.redhat.thermostat.common.command.Request.RequestType;
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.vm.jmx.common.JmxCommand;
+
+public class JmxToggleNotificationRequest {
+
+    private RequestQueue queue;
+
+    public JmxToggleNotificationRequest(RequestQueue queue) {
+        this.queue = queue;
+    }
+
+    public void sendEnableNotificationsRequestToAgent(VmRef vm, AgentInfoDAO agentDAO, boolean enable, RequestResponseListener responseListener) {
+
+        HostRef targetHostRef = vm.getAgent();
+
+        String address = agentDAO.getAgentInformation(targetHostRef).getConfigListenAddress();
+        String[] host = address.split(":");
+
+        InetSocketAddress target = new InetSocketAddress(host[0], Integer.parseInt(host[1]));
+        Request gcRequest = createRequest(target);
+
+        gcRequest.setReceiver(JmxCommand.RECEIVER);
+
+        gcRequest.setParameter(JmxCommand.class.getName(), enable ? JmxCommand.ENABLE_JMX_NOTIFICATIONS.name() : JmxCommand.DISABLE_JMX_NOTIFICATIONS.name());
+        gcRequest.setParameter(JmxCommand.VM_ID, vm.getIdString());
+
+        gcRequest.addListener(responseListener);
+
+        queue.putRequest(gcRequest);
+    }
+
+    // for testing
+    Request createRequest(InetSocketAddress target) {
+        return new Request(RequestType.RESPONSE_EXPECTED, target);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/client-core/src/test/java/com/redhat/thermostat/vm/jmx/client/core/internal/ActivatorTest.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.client.core.internal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.client.command.RequestQueue;
+import com.redhat.thermostat.client.core.InformationService;
+import com.redhat.thermostat.common.ApplicationService;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.testutils.StubBundleContext;
+import com.redhat.thermostat.vm.jmx.client.core.JmxNotificationsViewProvider;
+import com.redhat.thermostat.vm.jmx.common.JmxNotificationDAO;
+
+public class ActivatorTest {
+
+    @Test
+    public void testActivatorRegistersService() throws Exception {
+        StubBundleContext bundleContext = new StubBundleContext();
+
+        bundleContext.registerService(AgentInfoDAO.class, mock(AgentInfoDAO.class), null);
+        bundleContext.registerService(JmxNotificationDAO.class, mock(JmxNotificationDAO.class), null);
+        bundleContext.registerService(ApplicationService.class, mock(ApplicationService.class), null);
+        bundleContext.registerService(RequestQueue.class, mock(RequestQueue.class), null);
+        bundleContext.registerService(JmxNotificationsViewProvider.class, mock(JmxNotificationsViewProvider.class), null);
+
+        Activator activator = new Activator();
+
+        activator.start(bundleContext);
+
+        assertTrue(bundleContext.isServiceRegistered(InformationService.class.getName(), JmxNotificationsViewServiceImpl.class));
+
+        activator.stop(bundleContext);
+
+        assertFalse(bundleContext.isServiceRegistered(InformationService.class.getName(), JmxNotificationsViewServiceImpl.class));
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/client-core/src/test/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewControllerTest.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.client.core.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.net.InetSocketAddress;
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.client.command.RequestQueue;
+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.Timer;
+import com.redhat.thermostat.common.Timer.SchedulingType;
+import com.redhat.thermostat.common.TimerFactory;
+import com.redhat.thermostat.common.command.Request;
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.storage.model.AgentInformation;
+import com.redhat.thermostat.vm.jmx.client.core.JmxNotificationsView;
+import com.redhat.thermostat.vm.jmx.client.core.JmxNotificationsView.NotificationAction;
+import com.redhat.thermostat.vm.jmx.client.core.JmxNotificationsViewProvider;
+import com.redhat.thermostat.vm.jmx.common.JmxNotification;
+import com.redhat.thermostat.vm.jmx.common.JmxNotificationDAO;
+
+public class JmxNotificationsViewControllerTest {
+
+    private AgentInfoDAO agentDao;
+    private JmxNotificationDAO notificationDao;
+    private JmxNotificationsView view;
+    private JmxNotificationsViewProvider viewProvider;
+    private Timer timer;
+    private TimerFactory timerFactory;
+    private VmRef vm;
+    private RequestQueue queue;
+    private JmxNotificationsViewController controller;
+    private HostRef host;
+
+    @Before
+    public void setUp() {
+        agentDao = mock(AgentInfoDAO.class);
+        notificationDao = mock(JmxNotificationDAO.class);
+        queue = mock(RequestQueue.class);
+        view = mock(JmxNotificationsView.class);
+        viewProvider = mock(JmxNotificationsViewProvider.class);
+        when(viewProvider.createView()).thenReturn(view);
+        timer = mock(Timer.class);
+        timerFactory = mock(TimerFactory.class);
+        when(timerFactory.createTimer()).thenReturn(timer);
+
+        host = mock(HostRef.class);
+        vm = mock(VmRef.class);
+        when(vm.getAgent()).thenReturn(host);
+
+        controller = new JmxNotificationsViewController(agentDao, notificationDao, timerFactory, queue, viewProvider, vm);
+    }
+
+    @Test
+    public void verifyGetView() {
+        assertEquals(view, controller.getView());
+    }
+
+    @Test
+    public void verifyGetLocalizedName() {
+        assertEquals("Notifications", controller.getLocalizedName());
+    }
+
+    @Test
+    public void verifyTimerIsInitialized() {
+        verify(timer).setTimeUnit(TimeUnit.SECONDS);
+        verify(timer).setInitialDelay(0);
+        verify(timer).setDelay(5);
+        verify(timer).setSchedulingType(SchedulingType.FIXED_RATE);
+        verify(timer).setTimeUnit(TimeUnit.SECONDS);
+
+        ArgumentCaptor<Runnable> actionCaptor = ArgumentCaptor.forClass(Runnable.class);
+        verify(timer).setAction(actionCaptor.capture());
+
+        Runnable timerAction = actionCaptor.getValue();
+
+        JmxNotification data = mock(JmxNotification.class);
+        when(notificationDao.getNotifications(vm, Long.MIN_VALUE)).thenReturn(Arrays.asList(data));
+
+        timerAction.run();
+
+        verify(view).addNotification(data);
+
+    }
+
+    @Test
+    public void verifyTimerIsStartedWhenViewIsVisible() {
+
+        ArgumentCaptor<ActionListener> listenerCaptor = ArgumentCaptor.forClass(ActionListener.class);
+
+        verify(view).addActionListener(listenerCaptor.capture());
+
+        ActionListener listener = listenerCaptor.getValue();
+        listener.actionPerformed(new ActionEvent<Enum<?>>(view, Action.VISIBLE));
+
+        verify(timer).start();
+    }
+
+    @Test
+    public void verifyTimerIsStoppedWhenViewisInvisible() {
+        ArgumentCaptor<ActionListener> listenerCaptor = ArgumentCaptor.forClass(ActionListener.class);
+
+        verify(view).addActionListener(listenerCaptor.capture());
+
+        ActionListener listener = listenerCaptor.getValue();
+        listener.actionPerformed(new ActionEvent<Enum<?>>(view, Action.VISIBLE));
+
+        verify(timer).start();
+
+        listener.actionPerformed(new ActionEvent<Enum<?>>(view, Action.HIDDEN));
+
+        verify(timer).stop();
+
+    }
+
+    @Test
+    public void enableNotificationsWhenViewFiresEvent() {
+        ArgumentCaptor<ActionListener> listenerCaptor = ArgumentCaptor.forClass(ActionListener.class);
+
+        verify(view).addNotificationActionListener(listenerCaptor.capture());
+
+        AgentInformation agentInfo = mock(AgentInformation.class);
+        when(agentInfo.getConfigListenAddress()).thenReturn("example.com:0");
+        when(agentDao.getAgentInformation(host)).thenReturn(agentInfo);
+
+        listenerCaptor.getValue().actionPerformed(new ActionEvent<>(view, NotificationAction.TOGGLE_NOTIFICATIONS));
+
+        ArgumentCaptor<Request> requestCaptor = ArgumentCaptor.forClass(Request.class);
+        verify(queue).putRequest(requestCaptor.capture());
+
+        Request req = requestCaptor.getValue();
+        assertEquals(new InetSocketAddress("example.com", 0), req.getTarget());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/client-core/src/test/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewServiceImplTest.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.client.core.internal;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.client.command.RequestQueue;
+import com.redhat.thermostat.common.Ordered;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.TimerFactory;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.vm.jmx.client.core.JmxNotificationsView;
+import com.redhat.thermostat.vm.jmx.client.core.JmxNotificationsViewProvider;
+import com.redhat.thermostat.vm.jmx.common.JmxNotificationDAO;
+
+public class JmxNotificationsViewServiceImplTest {
+
+    private AgentInfoDAO agentDao;
+    private JmxNotificationDAO notificationDao;
+    private Timer timer;
+    private TimerFactory timerFactory;
+    private JmxNotificationsViewProvider viewProvider;
+    private JmxNotificationsViewServiceImpl service;
+    private JmxNotificationsView view;
+    private RequestQueue queue;
+
+    @Before
+    public void setUp() {
+        agentDao = mock(AgentInfoDAO.class);
+        notificationDao = mock(JmxNotificationDAO.class);
+        timer = mock(Timer.class);
+        timerFactory = mock(TimerFactory.class);
+        when(timerFactory.createTimer()).thenReturn(timer);
+
+        queue = mock(RequestQueue.class);
+
+        view = mock(JmxNotificationsView.class);
+        viewProvider = mock(JmxNotificationsViewProvider.class);
+        when(viewProvider.createView()).thenReturn(view);
+
+        service = new JmxNotificationsViewServiceImpl(agentDao, notificationDao, queue, timerFactory, viewProvider);
+    }
+
+    @Test
+    public void verifyGetFilter() {
+        assertNotNull(service.getFilter());
+    }
+
+    @Test
+    public void verifyGetInformationServiceController() {
+        VmRef vm = mock(VmRef.class);
+        assertNotNull(service.getInformationServiceController(vm));
+    }
+
+    @Test
+    public void verifyGetOrderValue() {
+        assertTrue(service.getOrderValue() > Ordered.ORDER_USER_GROUP);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/client-core/src/test/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxToggleNotificationRequestTest.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.client.core.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.net.InetSocketAddress;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.client.command.RequestQueue;
+import com.redhat.thermostat.common.command.Request;
+import com.redhat.thermostat.common.command.RequestResponseListener;
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.storage.model.AgentInformation;
+import com.redhat.thermostat.vm.jmx.common.JmxCommand;
+
+public class JmxToggleNotificationRequestTest {
+
+    private static final String HOST = "example.com";
+    private static final int PORT = 0;
+
+    private RequestQueue queue;
+    private ArgumentCaptor<Request> requestCaptor;
+    private HostRef host;
+    private VmRef vm;
+    private AgentInfoDAO agentDAO;
+    private RequestResponseListener responseListener;
+    private AgentInformation agentInfo;
+
+    @Before
+    public void setUp() {
+        queue = mock(RequestQueue.class);
+        requestCaptor = ArgumentCaptor.forClass(Request.class);
+
+        host = mock(HostRef.class);
+        vm = mock(VmRef.class);
+        when(vm.getAgent()).thenReturn(host);
+
+        agentDAO = mock(AgentInfoDAO.class);
+
+        agentInfo = mock(AgentInformation.class);
+        when(agentInfo.getConfigListenAddress()).thenReturn(HOST + ":" + PORT);
+        when(agentDAO.getAgentInformation(host)).thenReturn(agentInfo);
+
+        responseListener = mock(RequestResponseListener.class);
+    }
+
+    @Test
+    public void testEnableNotificationMessage() {
+        new JmxToggleNotificationRequest(queue).sendEnableNotificationsRequestToAgent(vm, agentDAO, true, responseListener);
+
+        verify(queue).putRequest(requestCaptor.capture());
+
+        Request req = requestCaptor.getValue();
+
+        assertEquals(new InetSocketAddress(HOST, PORT), req.getTarget());
+        assertEquals(JmxCommand.RECEIVER, req.getReceiver());
+        assertEquals(vm.getIdString(), req.getParameter(JmxCommand.VM_ID));
+
+        assertEquals(JmxCommand.ENABLE_JMX_NOTIFICATIONS.name(), req.getParameter(JmxCommand.class.getName()));
+    }
+
+    @Test
+    public void testDisableNotificationMessage() {
+        new JmxToggleNotificationRequest(queue).sendEnableNotificationsRequestToAgent(vm, agentDAO, false, responseListener);
+
+        verify(queue).putRequest(requestCaptor.capture());
+
+        Request req = requestCaptor.getValue();
+
+        assertEquals(new InetSocketAddress(HOST, PORT), req.getTarget());
+        assertEquals(JmxCommand.RECEIVER, req.getReceiver());
+        assertEquals(vm.getIdString(), req.getParameter(JmxCommand.VM_ID));
+
+        assertEquals(JmxCommand.DISABLE_JMX_NOTIFICATIONS.name(), req.getParameter(JmxCommand.class.getName()));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/client-swing/pom.xml	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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>
+    <artifactId>thermostat-vm-jmx</artifactId>
+    <groupId>com.redhat.thermostat</groupId>
+    <version>0.8.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>thermostat-vm-jmx-client-swing</artifactId>
+  <packaging>bundle</packaging>
+  <name>Thermostat VM JMX Swing Client plugin</name>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
+            <Bundle-SymbolicName>com.redhat.thermostat.vm.jmx.client.swing</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.vm.jmx.client.swing.internal.Activator</Bundle-Activator>
+            <Export-Package />
+            <Private-Package>
+              com.redhat.thermostat.vm.jmx.client.swing.internal
+            </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-test</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.easytesting</groupId>
+      <artifactId>fest-swing</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>net.java.openjdk.cacio</groupId>
+      <artifactId>cacio-tta</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.compendium</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-client-swing</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-jmx-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+        <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-jmx-client-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/client-swing/src/main/java/com/redhat/thermostat/vm/jmx/client/swing/internal/Activator.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.client.swing.internal;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+import com.redhat.thermostat.vm.jmx.client.core.JmxNotificationsViewProvider;
+
+public class Activator implements BundleActivator {
+
+    private ServiceRegistration viewProviderRegistration;
+
+    @Override
+    public void start(BundleContext context) throws Exception {
+        viewProviderRegistration = context.registerService(JmxNotificationsViewProvider.class, new JmxNotificationsSwingViewProvider(), null);
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        viewProviderRegistration.unregister();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/client-swing/src/main/java/com/redhat/thermostat/vm/jmx/client/swing/internal/JmxNotificationsSwingView.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.client.swing.internal;
+
+import java.awt.Component;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.swing.DefaultListModel;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
+
+import com.redhat.thermostat.client.swing.ComponentVisibleListener;
+import com.redhat.thermostat.client.swing.IconResource;
+import com.redhat.thermostat.client.swing.SwingComponent;
+import com.redhat.thermostat.client.swing.components.ActionToggleButton;
+import com.redhat.thermostat.client.swing.components.HeaderPanel;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.vm.jmx.client.core.JmxNotificationsView;
+import com.redhat.thermostat.vm.jmx.common.JmxNotification;
+
+public class JmxNotificationsSwingView extends JmxNotificationsView implements SwingComponent {
+
+    private List<ActionListener<NotificationAction>> listeners = new CopyOnWriteArrayList<>();
+
+    private final HeaderPanel visiblePanel;
+    private final DefaultListModel<String> listModel = new DefaultListModel<>();
+
+    private ActionToggleButton toolbarButton;
+
+    public JmxNotificationsSwingView() {
+
+        JPanel contents = new JPanel();
+        contents.setLayout(new GridBagLayout());
+        GridBagConstraints c = new GridBagConstraints();
+        c.weightx = 1;
+        c.weighty = 1;
+        c.fill = GridBagConstraints.BOTH;
+        c.anchor = GridBagConstraints.PAGE_START;
+
+        JList<String> issuesList = new JList<>(listModel);
+
+        contents.add(new JScrollPane(issuesList), c);
+
+        contents.addHierarchyListener(new ComponentVisibleListener() {
+            @Override
+            public void componentShown(Component component) {
+                notifier.fireAction(Action.VISIBLE);
+            }
+
+            @Override
+            public void componentHidden(Component component) {
+                notifier.fireAction(Action.HIDDEN);
+            }
+        });
+
+        toolbarButton = new ActionToggleButton(IconResource.RECORD.getIcon(), "");
+        toolbarButton.setName("toggleNotifications");
+        toolbarButton.addActionListener(new java.awt.event.ActionListener() {
+
+            @Override
+            public void actionPerformed(java.awt.event.ActionEvent e) {
+                fireNotificationAction(NotificationAction.TOGGLE_NOTIFICATIONS);
+            }
+        });
+
+        visiblePanel = new HeaderPanel("JMX Notifications");
+        visiblePanel.addToolBarButton(toolbarButton);
+        visiblePanel.setContent(contents);
+    }
+
+    @Override
+    public void addNotificationActionListener(ActionListener<NotificationAction> listener) {
+        listeners.add(listener);
+    }
+
+    @Override
+    public void removeNotificationActionListener(ActionListener<NotificationAction> listener) {
+        listeners.remove(listener);
+    }
+
+    private void fireNotificationAction(NotificationAction action) {
+        for (ActionListener<NotificationAction> listener : listeners) {
+            listener.actionPerformed(new ActionEvent<>(this, action));
+        }
+    }
+
+    @Override
+    public void setNotificationsEnabled(final boolean enabled) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                toolbarButton.setSelected(enabled);
+            }
+        });
+    }
+
+    @Override
+    public void clearNotifications() {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                listModel.clear();
+            }
+        });
+    }
+
+    @Override
+    public void addNotification(final JmxNotification data) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                listModel.add(listModel.size(), data.getContents());
+            }
+        });
+    }
+
+    @Override
+    public Component getUiComponent() {
+        return visiblePanel;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/client-swing/src/main/java/com/redhat/thermostat/vm/jmx/client/swing/internal/JmxNotificationsSwingViewProvider.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.client.swing.internal;
+
+import com.redhat.thermostat.vm.jmx.client.core.JmxNotificationsView;
+import com.redhat.thermostat.vm.jmx.client.core.JmxNotificationsViewProvider;
+
+public class JmxNotificationsSwingViewProvider implements JmxNotificationsViewProvider {
+
+    @Override
+    public JmxNotificationsView createView() {
+        return new JmxNotificationsSwingView();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/client-swing/src/test/java/com/redhat/thermostat/vm/jmx/client/swing/internal/AcitvatorTest.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.client.swing.internal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.testutils.StubBundleContext;
+import com.redhat.thermostat.vm.jmx.client.core.JmxNotificationsViewProvider;
+
+public class AcitvatorTest {
+
+    @Test
+    public void testActivator() throws Exception {
+        StubBundleContext bundleContext = new StubBundleContext();
+        Activator activator = new Activator();
+
+        activator.start(bundleContext);
+
+        assertTrue(bundleContext.isServiceRegistered(JmxNotificationsViewProvider.class.getName(), JmxNotificationsSwingViewProvider.class));
+
+        activator.stop(bundleContext);
+
+        assertFalse(bundleContext.isServiceRegistered(JmxNotificationsViewProvider.class.getName(), JmxNotificationsSwingViewProvider.class));
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/client-swing/src/test/java/com/redhat/thermostat/vm/jmx/client/swing/internal/JmxNotificationSwingViewTest.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.client.swing.internal;
+
+public class JmxNotificationSwingViewTest {
+    // TODO implement this
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/client-swing/src/test/java/com/redhat/thermostat/vm/jmx/client/swing/internal/JmxNotificationsSwingViewProviderTest.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.client.swing.internal;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class JmxNotificationsSwingViewProviderTest {
+
+    @Test
+    public void verifySwingViewCreation() {
+        assertTrue(new JmxNotificationsSwingViewProvider().createView() instanceof JmxNotificationsSwingView);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/common/pom.xml	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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>
+    <artifactId>thermostat-vm-jmx</artifactId>
+    <groupId>com.redhat.thermostat</groupId>
+    <version>0.8.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>thermostat-vm-jmx-common</artifactId>
+  <packaging>bundle</packaging>
+  <name>Thermostat VM JMX common code</name>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
+            <Bundle-SymbolicName>com.redhat.thermostat.vm.jmx.common</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.vm.jmx.common.internal.Activator</Bundle-Activator>
+            <Export-Package>
+              com.redhat.thermostat.vm.jmx.common,
+            </Export-Package>
+            <Private-Package>
+              com.redhat.thermostat.vm.jmx.common.internal,
+            </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-test</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.compendium</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-storage-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/common/src/main/java/com/redhat/thermostat/vm/jmx/common/JmxCommand.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.common;
+
+public enum JmxCommand {
+
+    ENABLE_JMX_NOTIFICATIONS,
+    DISABLE_JMX_NOTIFICATIONS,
+    ;
+
+    public static final String VM_ID = "VM_ID";
+    public static final String RECEIVER = "com.redhat.thermostat.vm.jmx.agent.internal.JmxRequestListener";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/common/src/main/java/com/redhat/thermostat/vm/jmx/common/JmxNotification.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.common;
+
+import com.redhat.thermostat.storage.core.Entity;
+import com.redhat.thermostat.storage.core.Persist;
+import com.redhat.thermostat.storage.model.BasePojo;
+import com.redhat.thermostat.storage.model.TimeStampedPojo;
+
+@Entity
+public class JmxNotification extends BasePojo implements TimeStampedPojo {
+
+    private long timeStamp;
+    private int vmId;
+    private String importance;
+    private String sourceBackend;
+    private String sourceDetails;
+    private String contents;
+
+    @Persist
+    public int getVmId() {
+        return vmId;
+    }
+
+    @Persist
+    public void setVmId(int vmId) {
+        this.vmId = vmId;
+    }
+
+    @Persist
+    public String getImportance() {
+        return importance;
+    }
+
+    @Persist
+    public void setImportance(String importance) {
+        this.importance = importance;
+    }
+
+    @Persist
+    public String getSourceBackend() {
+        return sourceBackend;
+    }
+
+    @Persist
+    public void setSourceBackend(String sourceBackend) {
+        this.sourceBackend = sourceBackend;
+    }
+
+    @Persist
+    public String getSourceDetails() {
+        return sourceDetails;
+    }
+
+    @Persist
+    public void setSourceDetails(String sourceDetails) {
+        this.sourceDetails = sourceDetails;
+    }
+
+    @Persist
+    public String getContents() {
+        return contents;
+    }
+
+    @Persist
+    public void setContents(String contents) {
+        this.contents = contents;
+    }
+
+    @Persist
+    @Override
+    public long getTimeStamp() {
+        return timeStamp;
+    }
+
+    @Persist
+    public void setTimeStamp(long timeStamp) {
+        this.timeStamp = timeStamp;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/common/src/main/java/com/redhat/thermostat/vm/jmx/common/JmxNotificationDAO.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.common;
+
+import java.util.List;
+
+import com.redhat.thermostat.storage.core.VmRef;
+
+public interface JmxNotificationDAO {
+
+    void addNotification(JmxNotification notification);
+
+    List<JmxNotification> getNotifications(VmRef notificationsFor, long timeStampSince);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/common/src/main/java/com/redhat/thermostat/vm/jmx/common/internal/Activator.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.common.internal;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.tracker.ServiceTracker;
+
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.vm.jmx.common.JmxNotificationDAO;
+
+public class Activator implements BundleActivator {
+
+    private ServiceTracker storageTracker;
+    private ServiceRegistration daoRegistration;
+
+    @Override
+    public void start(final BundleContext context) throws Exception {
+        storageTracker = new ServiceTracker(context, Storage.class, null) {
+            @Override
+            public Object addingService(ServiceReference reference) {
+                Storage storage = (Storage) super.addingService(reference);
+                JmxNotificationDAOImpl dao = new JmxNotificationDAOImpl(storage);
+                daoRegistration = context.registerService(JmxNotificationDAO.class, dao, null);
+                return storage;
+            }
+
+            @Override
+            public void removedService(ServiceReference reference, Object service) {
+                daoRegistration.unregister();
+                super.removedService(reference, service);
+            }
+        };
+        storageTracker.open();
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        storageTracker.close();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/common/src/main/java/com/redhat/thermostat/vm/jmx/common/internal/JmxNotificationDAOImpl.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.common.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.redhat.thermostat.storage.core.Add;
+import com.redhat.thermostat.storage.core.Category;
+import com.redhat.thermostat.storage.core.Cursor;
+import com.redhat.thermostat.storage.core.Key;
+import com.redhat.thermostat.storage.core.Query;
+import com.redhat.thermostat.storage.core.Query.Criteria;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.vm.jmx.common.JmxNotificationDAO;
+import com.redhat.thermostat.vm.jmx.common.JmxNotification;
+
+public class JmxNotificationDAOImpl implements JmxNotificationDAO {
+
+    // TODO: private static final Key IMPORTANCE = new Key<>("importance",
+    // false);
+
+    private static final Key<String> SOURCE_BACKEND = new Key<>("sourceBackend", false);
+    private static final Key<String> SOURCE_DESCRPTION = new Key<>("sourceDescription", false);
+    private static final Key<String> CONTENTS = new Key<>("contents", false);
+
+    static final Category<JmxNotification> NOTIFICATIONS =
+            new Category<>("vm-jmx-notification", JmxNotification.class,
+                    Key.AGENT_ID, Key.VM_ID, Key.TIMESTAMP,
+                    SOURCE_BACKEND, SOURCE_DESCRPTION, CONTENTS);
+
+    private Storage storage;
+
+    public JmxNotificationDAOImpl(Storage storage) {
+        this.storage = storage;
+        storage.registerCategory(NOTIFICATIONS);
+    }
+
+    @Override
+    public void addNotification(JmxNotification notification) {
+        Add add = storage.createAdd(NOTIFICATIONS);
+        add.setPojo(notification);
+        add.apply();
+    }
+
+    @Override
+    public List<JmxNotification> getNotifications(VmRef notificationsFor, long timeStampSince) {
+        Query<JmxNotification> query = storage.createQuery(NOTIFICATIONS);
+        query.where(Key.AGENT_ID, Criteria.EQUALS, notificationsFor.getAgent().getAgentId());
+        query.where(Key.VM_ID, Criteria.EQUALS, notificationsFor.getId());
+        query.where(Key.TIMESTAMP, Criteria.GREATER_THAN, timeStampSince);
+
+        List<JmxNotification> results = new ArrayList<>();
+        Cursor<JmxNotification> cursor = query.execute();
+        while (cursor.hasNext()) {
+            results.add(cursor.next());
+        }
+
+        return results;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/common/src/test/java/com/redhat/thermostat/vm/jmx/common/internal/ActivatorTest.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.common.internal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.testutils.StubBundleContext;
+import com.redhat.thermostat.vm.jmx.common.JmxNotificationDAO;
+
+public class ActivatorTest {
+
+    @Test
+    public void testActivator() throws Exception {
+        StubBundleContext bundleContext = new StubBundleContext();
+        Storage storage = mock(Storage.class);
+        bundleContext.registerService(Storage.class, storage, null);
+
+        Activator activator = new Activator();
+
+        assertFalse(bundleContext.isServiceRegistered(JmxNotificationDAO.class.getName(), JmxNotificationDAOImpl.class));
+
+        activator.start(bundleContext);
+
+        assertTrue(bundleContext.isServiceRegistered(JmxNotificationDAO.class.getName(), JmxNotificationDAOImpl.class));
+
+        activator.stop(bundleContext);
+
+        assertFalse(bundleContext.isServiceRegistered(JmxNotificationDAO.class.getName(), JmxNotificationDAOImpl.class));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/common/src/test/java/com/redhat/thermostat/vm/jmx/common/internal/JmxNotificationDAOImplTest.java	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.jmx.common.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.storage.core.Add;
+import com.redhat.thermostat.storage.core.Cursor;
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.core.Key;
+import com.redhat.thermostat.storage.core.Query;
+import com.redhat.thermostat.storage.core.Query.Criteria;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.vm.jmx.common.JmxNotification;
+
+public class JmxNotificationDAOImplTest {
+
+    private Storage storage;
+
+    private JmxNotificationDAOImpl dao;
+
+    @Before
+    public void setUp() {
+        storage = mock(Storage.class);
+
+        dao = new JmxNotificationDAOImpl(storage);
+    }
+
+    @Test
+    public void verfiyAddNotification() {
+        Add add = mock(Add.class);
+        when(storage.createAdd(JmxNotificationDAOImpl.NOTIFICATIONS)).thenReturn(add);
+
+        JmxNotification data = mock(JmxNotification.class);
+
+        dao.addNotification(data);
+
+        verify(add).setPojo(data);
+        verify(add).apply();
+        verifyNoMoreInteractions(add);
+    }
+
+    @Test
+    public void testGetNotificationsForVmSince() {
+        final String AGENT_ID = "an-agent's-id";
+        HostRef host = mock(HostRef.class);
+        when(host.getAgentId()).thenReturn(AGENT_ID);
+
+        final int VM_ID = -1;
+        VmRef vm = mock(VmRef.class);
+        when(vm.getAgent()).thenReturn(host);
+        when(vm.getId()).thenReturn(VM_ID);
+        long timeStamp = 10;
+
+        JmxNotification data = mock(JmxNotification.class);
+
+        Query query = mock(Query.class);
+        when(storage.createQuery(JmxNotificationDAOImpl.NOTIFICATIONS)).thenReturn(query);
+
+        Cursor cursor = mock(Cursor.class);
+        when(cursor.hasNext()).thenReturn(true).thenReturn(false);
+        when(cursor.next()).thenReturn(data).thenThrow(new AssertionError("not supposed to be called again"));
+
+        when(query.execute()).thenReturn(cursor);
+
+        List<JmxNotification> result = dao.getNotifications(vm, timeStamp);
+
+        verify(query).where(Key.AGENT_ID, Criteria.EQUALS, AGENT_ID);
+        verify(query).where(Key.VM_ID, Criteria.EQUALS, VM_ID);
+        verify(query).where(Key.TIMESTAMP, Criteria.GREATER_THAN, timeStamp);
+
+        assertEquals(1, result.size());
+        assertSame(data, result.get(0));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/distribution/pom.xml	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2012, 2013 Red Hat, Inc.
+
+ This file is part of Thermostat.
+
+ Thermostat is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ Thermostat is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Thermostat; see the file COPYING.  If not see
+ <http://www.gnu.org/licenses />.
+
+ Linking this code with other modules is making a combined work
+ based on this code.  Thus, the terms and conditions of the GNU
+ General Public License cover the whole combination.
+
+ As a special exception, the copyright holders of this code give
+ you permission to link this code with independent modules to
+ produce an executable, regardless of the license terms of these
+ independent modules, and to copy and distribute the resulting
+ executable under terms of your choice, provided that you also
+ meet, for each linked independent module, the terms and conditions
+ of the license of that module.  An independent module is a module
+ which is not derived from or based on this code.  If you modify
+ this code, you may extend this exception to your version of the
+ library, but you are not obligated to do so.  If you do not wish
+ to do so, delete this exception statement from your version.
+
+-->
+<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-vm-jmx</artifactId>
+    <version>0.8.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>thermostat-vm-jmx-distribution</artifactId>
+  <packaging>pom</packaging>
+
+  <name>Thermostat VM JMX plugin distribution</name>
+
+  <properties>
+    <thermostat.plugin>vm-jmx</thermostat.plugin>
+  </properties>
+
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <dependencies>
+          <dependency>
+            <groupId>com.redhat.thermostat</groupId>
+            <artifactId>thermostat-assembly</artifactId>
+            <version>${project.version}</version>
+          </dependency>
+        </dependencies>
+        <configuration>
+          <descriptorRefs>
+            <descriptorRef>plugin-assembly</descriptorRef>
+          </descriptorRefs>
+          <appendAssemblyId>false</appendAssemblyId>
+        </configuration>
+        <executions>
+          <execution>
+            <id>assemble-plugin</id>
+            <phase>package</phase>
+            <goals>
+              <goal>single</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-jmx-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-jmx-client-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-jmx-client-swing</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-jmx-agent</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+</project>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/distribution/thermostat-plugin.xml	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,59 @@
+<?xml version="1.0"?>
+<!--
+
+ Copyright 2012, 2013 Red Hat, Inc.
+
+ This file is part of Thermostat.
+
+ Thermostat is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ Thermostat is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Thermostat; see the file COPYING.  If not see
+ <http://www.gnu.org/licenses />.
+
+ Linking this code with other modules is making a combined work
+ based on this code.  Thus, the terms and conditions of the GNU
+ General Public License cover the whole combination.
+
+ As a special exception, the copyright holders of this code give
+ you permission to link this code with independent modules to
+ produce an executable, regardless of the license terms of these
+ independent modules, and to copy and distribute the resulting
+ executable under terms of your choice, provided that you also
+ meet, for each linked independent module, the terms and conditions
+ of the license of that module.  An independent module is a module
+ which is not derived from or based on this code.  If you modify
+ this code, you may extend this exception to your version of the
+ library, but you are not obligated to do so.  If you do not wish
+ to do so, delete this exception statement from your version.
+
+-->
+<plugin xmlns="http://icedtea.classpath.org/thermostat/plugins/v1.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://icedtea.classpath.org/thermostat/plugins/v1.0 thermostat-plugin.xsd">
+  <extensions>
+    <extension>
+      <name>gui</name>
+      <bundles>
+        <bundle>thermostat-vm-jmx-common-${project.version}.jar</bundle>
+        <bundle>thermostat-vm-jmx-client-core-${project.version}.jar</bundle>
+        <bundle>thermostat-vm-jmx-client-swing-${project.version}.jar</bundle>
+      </bundles>
+    </extension>
+    <extension>
+      <name>agent</name>
+      <bundles>
+        <bundle>thermostat-vm-jmx-agent-${project.version}.jar</bundle>
+        <bundle>thermostat-vm-jmx-common-${project.version}.jar</bundle>
+      </bundles>
+    </extension>
+  </extensions>
+</plugin>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-jmx/pom.xml	Fri May 17 14:22:28 2013 -0400
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2012, 2013 Red Hat, Inc.
+
+ This file is part of Thermostat.
+
+ Thermostat is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ Thermostat is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Thermostat; see the file COPYING.  If not see
+ <http://www.gnu.org/licenses />.
+
+ Linking this code with other modules is making a combined work
+ based on this code.  Thus, the terms and conditions of the GNU
+ General Public License cover the whole combination.
+
+ As a special exception, the copyright holders of this code give
+ you permission to link this code with independent modules to
+ produce an executable, regardless of the license terms of these
+ independent modules, and to copy and distribute the resulting
+ executable under terms of your choice, provided that you also
+ meet, for each linked independent module, the terms and conditions
+ of the license of that module.  An independent module is a module
+ which is not derived from or based on this code.  If you modify
+ this code, you may extend this exception to your version of the
+ library, but you are not obligated to do so.  If you do not wish
+ to do so, delete this exception statement from your version.
+
+-->
+<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.8.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>thermostat-vm-jmx</artifactId>
+  <packaging>pom</packaging>
+
+  <name>Thermostat JMX plugin</name>
+
+  <modules>
+    <module>agent</module>
+    <module>common</module>
+    <module>client-core</module>
+    <module>client-swing</module>
+    <module>distribution</module>
+  </modules>
+
+</project>
+