changeset 41:37f452bb08c5

client: pull data from mongodb
author Omair Majid <omajid@redhat.com>
date Tue, 17 Jan 2012 12:59:16 -0500
parents 55c100fdb147
children 89f821df2f1b
files build.xml src/com/redhat/thermostat/client/Connection.java src/com/redhat/thermostat/client/DummyUiFacadeFactory.java src/com/redhat/thermostat/client/HostPanelFacadeImpl.java src/com/redhat/thermostat/client/Main.java src/com/redhat/thermostat/client/MainWindowFacadeImpl.java src/com/redhat/thermostat/client/MongoConnection.java src/com/redhat/thermostat/client/SummaryPanelFacadeImpl.java src/com/redhat/thermostat/client/UiFacadeFactory.java src/com/redhat/thermostat/client/UiFacadeFactoryImpl.java src/com/redhat/thermostat/client/VmPanelFacadeImpl.java src/com/redhat/thermostat/client/strings.properties src/com/redhat/thermostat/client/ui/MainWindow.java
diffstat 13 files changed, 643 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/build.xml	Mon Jan 16 14:13:40 2012 -0500
+++ b/build.xml	Tue Jan 17 12:59:16 2012 -0500
@@ -42,6 +42,7 @@
     <path id="client-gui.classpath">
         <pathelement path="${jcommon.jar}" />
         <pathelement path="${jfreechart.jar}" />
+        <pathelement path="${mongo.jar}" />
     </path>
 
     <path id="test.classpath">
--- a/src/com/redhat/thermostat/client/Connection.java	Mon Jan 16 14:13:40 2012 -0500
+++ b/src/com/redhat/thermostat/client/Connection.java	Tue Jan 17 12:59:16 2012 -0500
@@ -1,5 +1,8 @@
 package com.redhat.thermostat.client;
 
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
 public abstract class Connection {
 
     public enum ConnectionType {
@@ -28,9 +31,23 @@
         }
     }
 
+    public enum ConnectionStatus {
+        CONNECTED,
+        FAILED_TO_CONNECT,
+        DISCONNECTED,
+    }
+
+    public interface ConnectionListener {
+        public void changed(ConnectionStatus newStatus);
+    }
+
+    protected boolean connected = false;
+
     private ConnectionType type;
     private String url;
 
+    private List<ConnectionListener> listeners = new CopyOnWriteArrayList<ConnectionListener>();
+
     public void setType(ConnectionType type) {
         this.type = type;
     }
@@ -58,4 +75,22 @@
     public abstract void connect();
 
     public abstract void disconnect();
+
+    public boolean isConnected() {
+        return connected;
+    }
+
+    public void addListener(ConnectionListener listener) {
+        this.listeners.add(listener);
+    }
+
+    public void removeListener(ConnectionListener listener) {
+        this.listeners.remove(listener);
+    }
+
+    protected void fireChanged(ConnectionStatus status) {
+        for (ConnectionListener listener: listeners) {
+            listener.changed(status);
+        }
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/redhat/thermostat/client/DummyUiFacadeFactory.java	Tue Jan 17 12:59:16 2012 -0500
@@ -0,0 +1,31 @@
+package com.redhat.thermostat.client;
+
+public class DummyUiFacadeFactory implements UiFacadeFactory {
+
+    private final DummyFacade dummyFacade;
+
+    public DummyUiFacadeFactory() {
+        dummyFacade = new DummyFacade();
+    }
+
+    @Override
+    public MainWindowFacade getMainWindow() {
+        return dummyFacade;
+    }
+
+    @Override
+    public SummaryPanelFacade getSummaryPanel() {
+        return dummyFacade;
+    }
+
+    @Override
+    public HostPanelFacade getHostPanel(HostRef ref) {
+        return dummyFacade;
+    }
+
+    @Override
+    public VmPanelFacade getVmPanel(VmRef ref) {
+        return dummyFacade;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/redhat/thermostat/client/HostPanelFacadeImpl.java	Tue Jan 17 12:59:16 2012 -0500
@@ -0,0 +1,135 @@
+package com.redhat.thermostat.client;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DB;
+import com.mongodb.DBCollection;
+import com.mongodb.DBCursor;
+import com.mongodb.DBObject;
+import com.redhat.thermostat.common.HostInfo;
+import com.redhat.thermostat.common.NetworkInfo;
+import com.redhat.thermostat.common.NetworkInterfaceInfo;
+
+public class HostPanelFacadeImpl implements HostPanelFacade {
+
+    private HostRef agent;
+    private DB db;
+    private DBCollection hostInfoCollection;
+    private DBCollection networkInfoCollection;
+    private DBCollection cpuStatsCollection;
+    private DBCollection memoryStatsCollection;
+
+    private Set<MemoryType> toDisplay = new HashSet<MemoryType>();
+
+    public HostPanelFacadeImpl(HostRef ref, DB db) {
+        this.agent = ref;
+        this.db = db;
+
+        hostInfoCollection = db.getCollection("host-info");
+        networkInfoCollection = db.getCollection("network-info");
+        cpuStatsCollection = db.getCollection("cpu-stats");
+        memoryStatsCollection = db.getCollection("memory-stats");
+
+        toDisplay.addAll(Arrays.asList(MemoryType.values()));
+    }
+
+    /*
+     * Host-related methods
+     */
+
+    @Override
+    public HostInfo getHostInfo() {
+        DBObject hostInfo = hostInfoCollection.findOne(new BasicDBObject("agent-id", agent.getAgentId()));
+        String hostName = (String) hostInfo.get("hostname");
+        String osName = (String) hostInfo.get("os_name");
+        String osKernel = (String) hostInfo.get("os_kernel");
+        String cpuModel = (String) hostInfo.get("cpu_model");
+        String cpuCount = (String) hostInfo.get("cpu_num");
+        String memoryTotal = (String) hostInfo.get("memory_total");
+        return new HostInfo(hostName, osName, osKernel, cpuModel, Integer.valueOf(cpuCount), Long.valueOf(memoryTotal));
+    }
+
+    @Override
+    public NetworkInfo getNetworkInfo() {
+        NetworkInfo network = new NetworkInfo();
+        DBCursor cursor = networkInfoCollection.find(new BasicDBObject("agent-id", agent.getAgentId()));
+        while (cursor.hasNext()) {
+            DBObject iface = cursor.next();
+            NetworkInterfaceInfo info = new NetworkInterfaceInfo((String) iface.get("iface"));
+            if (iface.containsField("ipv4addr")) {
+                info.setIp4Addr((String) iface.get("ipv4addr"));
+            }
+            if (iface.containsField("ipv6addr")) {
+                info.setIp6Addr((String) iface.get("ipv6addr"));
+            }
+            network.addNetworkInterfaceInfo(info);
+        }
+
+        return network;
+    }
+
+    @Override
+    public DiscreteTimeData<Double>[] getCpuLoad() {
+        List<DiscreteTimeData<Double>> load = new ArrayList<DiscreteTimeData<Double>>();
+        DBCursor cursor = cpuStatsCollection.find(new BasicDBObject("agent-id", agent.getAgentId()));
+        long timestamp = 0;
+        double data = 0;
+        while (cursor.hasNext()) {
+            DBObject stat = cursor.next();
+            timestamp = Long.valueOf((String) stat.get("timestamp"));
+            data = Double.valueOf((String) stat.get("5load"));
+            load.add(new DiscreteTimeData<Double>(timestamp, data));
+        }
+        // TODO we may also want to avoid sending out thousands of values.
+        // a subset of values from this entire array should suffice.
+        return (DiscreteTimeData<Double>[]) load.toArray(new DiscreteTimeData<?>[0]);
+    }
+
+    @Override
+    public DiscreteTimeData<Long>[] getMemoryUsage(MemoryType type) {
+        List<DiscreteTimeData<Long>> data = new ArrayList<DiscreteTimeData<Long>>();
+        DBCursor cursor = memoryStatsCollection.find(new BasicDBObject("agent-id", agent.getAgentId()));
+        long timestamp = 0;
+        long memoryData = 0;
+        while (cursor.hasNext()) {
+            DBObject stat = cursor.next();
+            timestamp = Long.valueOf((String) stat.get("timestamp"));
+            if (type.getInternalName().equals("used")) {
+                memoryData = Long.valueOf((String) stat.get("total")) - Long.valueOf((String) stat.get("free"));
+            } else {
+                memoryData = Long.valueOf((String) stat.get(type.getInternalName()));
+            }
+            data.add(new DiscreteTimeData<Long>(timestamp, memoryData));
+        }
+        // TODO we may also want to avoid sending out thousands of values.
+        // a subset of the values from this entire array should suffice.
+        return (DiscreteTimeData<Long>[]) data.toArray(new DiscreteTimeData<?>[0]);
+    }
+
+    @Override
+    public MemoryType[] getMemoryTypesToDisplay() {
+        return toDisplay.toArray(new MemoryType[0]);
+    }
+
+    @Override
+    public boolean isMemoryTypeDisplayed(MemoryType type) {
+        return toDisplay.contains(type);
+    }
+
+    @Override
+    public void setDisplayMemoryType(MemoryType type, boolean selected) {
+        if (selected) {
+            toDisplay.add(type);
+        } else {
+            toDisplay.remove(type);
+        }
+    }
+
+
+
+}
--- a/src/com/redhat/thermostat/client/Main.java	Mon Jan 16 14:13:40 2012 -0500
+++ b/src/com/redhat/thermostat/client/Main.java	Tue Jan 17 12:59:16 2012 -0500
@@ -1,15 +1,20 @@
 package com.redhat.thermostat.client;
 
+import static com.redhat.thermostat.client.Translate._;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import javax.swing.JFrame;
+import javax.swing.JOptionPane;
 import javax.swing.JPopupMenu;
 import javax.swing.SwingUtilities;
 
+import com.redhat.thermostat.client.Connection.ConnectionListener;
+import com.redhat.thermostat.client.Connection.ConnectionStatus;
 import com.redhat.thermostat.client.Connection.ConnectionType;
 import com.redhat.thermostat.client.ui.ConnectionSelectionDialog;
 import com.redhat.thermostat.client.ui.MainWindow;
+import com.redhat.thermostat.common.Constants;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 
 public class Main {
@@ -17,11 +22,11 @@
     private static final Logger logger = LoggingUtils.getLogger(Main.class);
 
     private ClientArgs arguments;
+    private Connection connection;
+    private UiFacadeFactory uiFacadeFactory;
 
-    private void showGui() {
-        JPopupMenu.setDefaultLightWeightPopupEnabled(false);
-
-        Connection connection;
+    private Main(String[] args) {
+        this.arguments = new ClientArgs(args);
 
         if (arguments.useDummyDataSource()) {
             logger.log(Level.CONFIG, "using dummy data");
@@ -37,8 +42,12 @@
                 }
             };
         } else {
-            connection = null; // TODO replace with actual connection object
+            connection = new MongoConnection();
         }
+    }
+
+    private void showGui() {
+        JPopupMenu.setDefaultLightWeightPopupEnabled(false);
 
         ConnectionSelectionDialog dialog = new ConnectionSelectionDialog((JFrame) null, connection);
         dialog.pack();
@@ -49,10 +58,31 @@
             return;
         }
 
+        ConnectionListener connectionListener = new ConnectionListener() {
+            @Override
+            public void changed(ConnectionStatus newStatus) {
+                if (newStatus == ConnectionStatus.FAILED_TO_CONNECT) {
+                    JOptionPane.showMessageDialog(
+                            null,
+                            _("CONNECTION_FAILED_TO_CONNECT_DESCRIPTION"),
+                            _("CONNECTION_FAILED_TO_CONNECT_TITLE"),
+                            JOptionPane.ERROR_MESSAGE);
+                    System.exit(Constants.EXIT_UNABLE_TO_CONNECT_TO_DATABASE);
+                }
+            }
+        };
+
+        connection.addListener(connectionListener);
         connection.connect();
-        UiFacadeFactory.setConnection(connection);
+        connection.removeListener(connectionListener);
 
-        MainWindow gui = new MainWindow(UiFacadeFactory.getMainWindow());
+        if (arguments.useDummyDataSource()) {
+            uiFacadeFactory = new DummyUiFacadeFactory();
+        } else {
+            uiFacadeFactory = new UiFacadeFactoryImpl((MongoConnection) connection);
+        }
+
+        MainWindow gui = new MainWindow(uiFacadeFactory);
         gui.pack();
         gui.setVisible(true);
     }
@@ -60,8 +90,7 @@
     public static void main(String[] args) {
         LoggingUtils.setGlobalLogLevel(Level.ALL);
 
-        final Main main = new Main();
-        main.initArgs(args);
+        final Main main = new Main(args);
         SwingUtilities.invokeLater(new Runnable() {
             @Override
             public void run() {
@@ -70,7 +99,4 @@
         });
     }
 
-    public void initArgs(String[] args) {
-        this.arguments = new ClientArgs(args);
-    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/redhat/thermostat/client/MainWindowFacadeImpl.java	Tue Jan 17 12:59:16 2012 -0500
@@ -0,0 +1,61 @@
+package com.redhat.thermostat.client;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DB;
+import com.mongodb.DBCollection;
+import com.mongodb.DBCursor;
+import com.mongodb.DBObject;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+
+public class MainWindowFacadeImpl implements MainWindowFacade {
+
+    private static final Logger logger = LoggingUtils.getLogger(MainWindowFacadeImpl.class);
+
+    private DB db;
+    private DBCollection agentConfigCollection;
+    private DBCollection vmInfoCollection;
+
+    public MainWindowFacadeImpl(DB db) {
+        this.db = db;
+        this.agentConfigCollection = db.getCollection("agent-config");
+        this.vmInfoCollection = db.getCollection("vm-info");
+    }
+
+    @Override
+    public HostRef[] getHosts() {
+        List<HostRef> hostRefs = new ArrayList<HostRef>();
+
+        DBCursor cursor = agentConfigCollection.find();
+        while (cursor.hasNext()) {
+            DBObject doc = cursor.next();
+            String id = (String) doc.get("agent-id");
+            if (id != null) {
+                HostRef agent = new HostRef(id, id);
+                hostRefs.add(agent);
+            }
+        }
+        logger.log(Level.FINER, "found " + hostRefs.size() + " connected agents");
+        return hostRefs.toArray(new HostRef[0]);
+    }
+
+    @Override
+    public VmRef[] getVms(HostRef hostRef) {
+        List<VmRef> vmRefs = new ArrayList<VmRef>();
+        DBCursor cursor = vmInfoCollection.find(new BasicDBObject("agent-id", hostRef.getAgentId()));
+        while (cursor.hasNext()) {
+            DBObject vmObject = cursor.next();
+            String id = (String) vmObject.get("vm-id");
+            VmRef ref = new VmRef(hostRef, id, id);
+            vmRefs.add(ref);
+        }
+
+        return vmRefs.toArray(new VmRef[0]);
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/redhat/thermostat/client/MongoConnection.java	Tue Jan 17 12:59:16 2012 -0500
@@ -0,0 +1,89 @@
+package com.redhat.thermostat.client;
+
+import java.net.UnknownHostException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.mongodb.DB;
+import com.mongodb.Mongo;
+import com.mongodb.MongoException;
+import com.mongodb.MongoURI;
+import com.redhat.thermostat.agent.storage.StorageConstants;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+
+public class MongoConnection extends Connection {
+    private static final Logger logger = LoggingUtils.getLogger(MongoConnection.class);
+
+    private Mongo m = null;
+    private DB db = null;
+    private boolean isLocal = false;
+
+    @Override
+    public void connect() {
+        switch (getType()) {
+            case LOCAL:
+                try {
+                    m = new Mongo(getMongoURI());
+                    db = m.getDB(StorageConstants.THERMOSTAT_DB_NAME);
+                    /* the mongo java driver does not ensure this connection is actually working */
+                    testConnnection(db);
+                } catch (MongoException e) {
+                    fireChanged(ConnectionStatus.FAILED_TO_CONNECT);
+                    return;
+                } catch (UnknownHostException e) {
+                    fireChanged(ConnectionStatus.FAILED_TO_CONNECT);
+                    return;
+                }
+                isLocal = true;
+                break;
+            case REMOTE:
+                // TODO connect to a remote machine
+                break;
+            case CLUSTER:
+                // TODO connect to a cluster
+                break;
+            case NONE:
+                throw new IllegalArgumentException();
+        }
+        fireChanged(ConnectionStatus.CONNECTED);
+        connected = true;
+    }
+
+    private static void testConnnection(DB db) {
+        db.getCollection("agent-config").getCount();
+    }
+
+    private MongoURI getMongoURI() {
+        // FIXME hardcorded address
+        startMongoAndAgent();
+        return new MongoURI("mongodb://127.0.0.1:27017");
+    }
+
+    private void startMongoAndAgent() {
+        // TODO implement this
+        logger.log(Level.WARNING, "startMongoAndAgent not implemented");
+        logger.log(Level.WARNING, "please start mongodb and agent yourself");
+    }
+
+    public DB getDB() {
+        return db;
+    }
+
+    @Override
+    public void disconnect() {
+        if (m != null) {
+            m.close();
+        }
+        if (isLocal) {
+            stopMongoAndAgent();
+        }
+        connected = false;
+    }
+
+    private void stopMongoAndAgent() {
+        // TODO implement this
+        logger.log(Level.WARNING, "startMongoAndAgent not implemented");
+        logger.log(Level.WARNING, "please stop mongodb and agent yourself");
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/redhat/thermostat/client/SummaryPanelFacadeImpl.java	Tue Jan 17 12:59:16 2012 -0500
@@ -0,0 +1,36 @@
+package com.redhat.thermostat.client;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.mongodb.DB;
+import com.mongodb.DBCollection;
+
+public class SummaryPanelFacadeImpl implements SummaryPanelFacade {
+
+    private DB db;
+    private DBCollection agentConfigCollection;
+    private DBCollection vmInfoCollection;
+
+    public SummaryPanelFacadeImpl(DB db) {
+        this.db = db;
+        this.agentConfigCollection = db.getCollection("agent-config");
+        this.vmInfoCollection = db.getCollection("vm-info");
+    }
+
+    @Override
+    public long getTotalConnectedVms() {
+        return vmInfoCollection.getCount();
+    }
+
+    @Override
+    public long getTotalConnectedAgents() {
+        return agentConfigCollection.getCount();
+    }
+
+    @Override
+    public List<String> getIssues() {
+        return new ArrayList<String>();
+    }
+
+}
--- a/src/com/redhat/thermostat/client/UiFacadeFactory.java	Mon Jan 16 14:13:40 2012 -0500
+++ b/src/com/redhat/thermostat/client/UiFacadeFactory.java	Tue Jan 17 12:59:16 2012 -0500
@@ -1,36 +1,13 @@
 package com.redhat.thermostat.client;
 
-public class UiFacadeFactory {
-
-    private static Connection connection;
-
-    private static DummyFacade dummy;
+public interface UiFacadeFactory {
 
-    public static MainWindowFacade getMainWindow() {
-        return getDummy();
-    }
-
-    public static SummaryPanelFacade getSummaryPanel() {
-        return getDummy();
-    }
+    public MainWindowFacade getMainWindow();
 
-    public static HostPanelFacade getHostPanel(HostRef ref) {
-        return getDummy();
-    }
-
-    public static VmPanelFacade getVmPanel(VmRef ref) {
-        return getDummy();
-    }
+    public SummaryPanelFacade getSummaryPanel();
 
-    public static void setConnection(Connection connection) {
-        UiFacadeFactory.connection = connection;
-    }
+    public HostPanelFacade getHostPanel(HostRef ref);
 
-    private static DummyFacade getDummy() {
-        if (dummy == null) {
-            dummy = new DummyFacade();
-        }
-        return dummy;
-    }
+    public VmPanelFacade getVmPanel(VmRef ref);
 
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/redhat/thermostat/client/UiFacadeFactoryImpl.java	Tue Jan 17 12:59:16 2012 -0500
@@ -0,0 +1,34 @@
+package com.redhat.thermostat.client;
+
+public class UiFacadeFactoryImpl implements UiFacadeFactory {
+
+    private MongoConnection connection;
+
+    public UiFacadeFactoryImpl(MongoConnection connection) {
+        this.connection = connection;
+    }
+
+    @Override
+    public MainWindowFacade getMainWindow() {
+        return new MainWindowFacadeImpl(connection.getDB());
+    }
+
+    @Override
+    public SummaryPanelFacade getSummaryPanel() {
+        return new SummaryPanelFacadeImpl(connection.getDB());
+
+    }
+
+    @Override
+    public HostPanelFacade getHostPanel(HostRef ref) {
+        return new HostPanelFacadeImpl(ref, connection.getDB());
+
+    }
+
+    @Override
+    public VmPanelFacade getVmPanel(VmRef ref) {
+        return new VmPanelFacadeImpl(ref, connection.getDB());
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/redhat/thermostat/client/VmPanelFacadeImpl.java	Tue Jan 17 12:59:16 2012 -0500
@@ -0,0 +1,168 @@
+package com.redhat.thermostat.client;
+
+import static com.redhat.thermostat.client.Translate._;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DB;
+import com.mongodb.DBCollection;
+import com.mongodb.DBCursor;
+import com.mongodb.DBObject;
+import com.redhat.thermostat.common.VmInfo;
+import com.redhat.thermostat.common.VmMemoryStat;
+import com.redhat.thermostat.common.VmMemoryStat.Generation;
+import com.redhat.thermostat.common.VmMemoryStat.Space;
+
+public class VmPanelFacadeImpl implements VmPanelFacade {
+
+    private VmRef ref;
+    private DB db;
+    private DBCollection vmInfoCollection;
+    private DBCollection vmGcStatsCollection;
+    private DBCollection vmMemoryStatsCollection;
+
+    private VmMemoryStat cached;
+
+    public VmPanelFacadeImpl(VmRef vmRef, DB db) {
+        this.ref = vmRef;
+        this.db = db;
+        vmInfoCollection = db.getCollection("vm-info");
+        vmGcStatsCollection = db.getCollection("vm-gc-stats");
+        vmMemoryStatsCollection = db.getCollection("vm-memory-stats");
+
+    }
+
+    @Override
+    public VmInfo getVmInfo() {
+        BasicDBObject queryObject = new BasicDBObject();
+        queryObject.put("agent-id", ref.getAgent().getAgentId());
+        queryObject.put("vm-id", ref.getId());
+        DBObject vmInfoObject = vmInfoCollection.findOne(queryObject);
+        int vmPid = Integer.valueOf((String) vmInfoObject.get("vm-pid"));
+        long startTime = Long.valueOf((String) vmInfoObject.get("start-time"));
+        long stopTime = Long.valueOf((String) vmInfoObject.get("stop-time"));
+        String javaVersion = (String) vmInfoObject.get("runtime-version");
+        String javaHome = (String) vmInfoObject.get("java-home");
+        String mainClass = (String) vmInfoObject.get("main-class");
+        String commandLine = (String) vmInfoObject.get("command-line");
+        String vmName = (String) vmInfoObject.get("vm-name");
+        String vmInfo = (String) vmInfoObject.get("vm-info");
+        String vmVersion = (String) vmInfoObject.get("vm-version");
+        String vmArguments = (String) vmInfoObject.get("vm-arguments");
+        // TODO fill in these
+        Map<String, String> properties = new HashMap<String, String>();
+        Map<String, String> environment = new HashMap<String, String>();
+        List<String> loadedNativeLibraries = new ArrayList<String>();
+        return new VmInfo(vmPid, startTime, stopTime,
+                javaVersion, javaHome,
+                mainClass, commandLine,
+                vmName, vmInfo, vmVersion, vmArguments,
+                properties, environment, loadedNativeLibraries);
+
+    }
+
+    @Override
+    public String[] getCollectorNames() {
+        BasicDBObject queryObject = new BasicDBObject();
+        queryObject.put("agent-id", ref.getAgent().getAgentId());
+        queryObject.put("vm-id", ref.getId());
+        List results = vmGcStatsCollection.distinct("collector", queryObject);
+        List<String> collectorNames = new ArrayList<String>(results);
+
+        return collectorNames.toArray(new String[0]);
+    }
+
+    @Override
+    public DiscreteTimeData<Long>[] getCollectorRunTime(String collectorName) {
+        ArrayList<DiscreteTimeData<Long>> result = new ArrayList<DiscreteTimeData<Long>>();
+        BasicDBObject queryObject = new BasicDBObject();
+        queryObject.put("agent-id", ref.getAgent().getAgentId());
+        queryObject.put("vm-id", ref.getId());
+        queryObject.put("collector", collectorName);
+        DBCursor cursor = vmGcStatsCollection.find(queryObject);
+        long timestamp;
+        long walltime;
+        while (cursor.hasNext()) {
+            DBObject current = cursor.next();
+            timestamp = Long.valueOf((String) current.get("timestamp"));
+            walltime = Long.valueOf((String) current.get("wall-time"));
+            result.add(new DiscreteTimeData<Long>(timestamp, walltime));
+        }
+
+        return (DiscreteTimeData<Long>[]) result.toArray(new DiscreteTimeData<?>[0]);
+    }
+
+    @Override
+    public String getCollectorGeneration(String collectorName) {
+        if (cached == null) {
+            getLatestMemoryInfo();
+        }
+        for (Generation g: cached.getGenerations()) {
+            if (g.collector.equals(collectorName)) {
+                return g.name;
+            }
+        }
+        return _("UNKNOWN_GEN");
+    }
+
+    @Override
+    public VmMemoryStat getLatestMemoryInfo() {
+        BasicDBObject query = new BasicDBObject();
+        query.put("agent-id", ref.getAgent().getAgentId());
+        query.put("vm-id", ref.getId());
+        // TODO ensure timestamp is an indexed column
+        BasicDBObject sortByTimeStamp = new BasicDBObject("timestamp", -1);
+        DBCursor cursor;
+        cursor = vmMemoryStatsCollection.find(query).sort(sortByTimeStamp).limit(1);
+        if (cursor.hasNext()) {
+            DBObject current = cursor.next();
+            return createVmMemoryStatFromDBObject(current);
+        }
+
+        return null;
+    }
+
+    private VmMemoryStat createVmMemoryStatFromDBObject(DBObject dbObj) {
+        // FIXME so much hardcoding :'(
+
+        String[] spaceNames = new String[] { "eden", "s0", "s1", "old", "perm" };
+        List<Generation> generations = new ArrayList<Generation>();
+
+        long timestamp = Long.valueOf((String)dbObj.get("timestamp"));
+        int vmId = Integer.valueOf((String)dbObj.get("vm-id"));
+        for (String spaceName: spaceNames) {
+            DBObject info = (DBObject) dbObj.get(spaceName);
+            String generationName = (String) info.get("gen");
+            Generation target = null;
+            for (Generation generation: generations) {
+                if (generation.name.equals(generationName)) {
+                    target = generation;
+                }
+            }
+            if (target == null) {
+                target = new Generation();
+                target.name = generationName;
+                generations.add(target);
+            }
+            if (target.collector == null) {
+                target.collector = (String) info.get("collector");
+            }
+            Space space = new Space();
+            space.name = spaceName;
+            space.capacity = Long.valueOf((String)info.get("capacity"));
+            space.maxCapacity = Long.valueOf((String)info.get("max-capacity"));
+            space.used = Long.valueOf((String)info.get("used"));
+            if (target.spaces == null) {
+                target.spaces = new ArrayList<Space>();
+            }
+            target.spaces.add(space);
+        }
+
+        cached = new VmMemoryStat(timestamp, vmId , generations);
+        return cached;
+    }
+
+}
--- a/src/com/redhat/thermostat/client/strings.properties	Mon Jan 16 14:13:40 2012 -0500
+++ b/src/com/redhat/thermostat/client/strings.properties	Tue Jan 17 12:59:16 2012 -0500
@@ -1,3 +1,6 @@
+CONNECTION_FAILED_TO_CONNECT_TITLE = Failed to connect to data collector
+CONNECTION_FAILED_TO_CONNECT_DESCRIPTION = Thermostat failed to connect to the data collector.
+
 MAIN_WINDOW_TITLE = Thermostat
 
 MAIN_WINDOW_TREE_ROOT_NAME = Thermostat
--- a/src/com/redhat/thermostat/client/ui/MainWindow.java	Mon Jan 16 14:13:40 2012 -0500
+++ b/src/com/redhat/thermostat/client/ui/MainWindow.java	Tue Jan 17 12:59:16 2012 -0500
@@ -54,17 +54,19 @@
     private final DefaultMutableTreeNode root = new DefaultMutableTreeNode(_("MAIN_WINDOW_TREE_ROOT_NAME"));
     private final DefaultTreeModel treeModel = new DefaultTreeModel(root);
 
+    private final UiFacadeFactory facadeFactory;
     private final MainWindowFacade facade;
 
     private JPanel contentArea = null;
     private JTree agentVmTree = null;
     private JTextField searchField = null;
 
-    public MainWindow(MainWindowFacade facade) {
+    public MainWindow(UiFacadeFactory facadeFactory) {
         super();
         setTitle(_("MAIN_WINDOW_TITLE"));
 
-        this.facade = facade;
+        this.facadeFactory = facadeFactory;
+        this.facade = facadeFactory.getMainWindow();
 
         searchField = new JTextField();
         agentVmTree = new AgentVmTree(treeModel);
@@ -181,14 +183,14 @@
                     contentArea.removeAll();
                     TreePath path = e.getPath();
                     if (path.getPathCount() == 1) {/* root */
-                        contentArea.add(new SummaryPanel(UiFacadeFactory.getSummaryPanel()));
+                        contentArea.add(new SummaryPanel(facadeFactory.getSummaryPanel()));
                     } else if (path.getPathCount() == 2) { /* agent */
                         HostRef hostRef = (HostRef) ((DefaultMutableTreeNode) path.getLastPathComponent()).getUserObject();
-                        HostPanel panel = new HostPanel(UiFacadeFactory.getHostPanel(hostRef));
+                        HostPanel panel = new HostPanel(facadeFactory.getHostPanel(hostRef));
                         contentArea.add(panel);
                     } else { /* vm */
                         VmRef vmRef = (VmRef) ((DefaultMutableTreeNode) path.getLastPathComponent()).getUserObject();
-                        VmPanel panel = new VmPanel(UiFacadeFactory.getVmPanel(vmRef));
+                        VmPanel panel = new VmPanel(facadeFactory.getVmPanel(vmRef));
                         contentArea.add(panel);
                     }
                     // Fixes some 'ghosting' caused by the previous components