changeset 233:023cdedfdb8c

Merge
author Roman Kennke <rkennke@redhat.com>
date Mon, 16 Apr 2012 15:41:08 +0200
parents 581d71ba9125 (current diff) 9d74f4672d6c (diff)
children 001b59e74e8d 2bdcc631a4dc
files common/src/main/java/com/redhat/thermostat/cli/AppContextSetupImpl.java common/src/main/java/com/redhat/thermostat/common/dao/Connection.java common/src/main/java/com/redhat/thermostat/common/dao/ConnectionProvider.java common/src/main/java/com/redhat/thermostat/common/dao/MongoConnection.java common/src/main/java/com/redhat/thermostat/common/dao/MongoConnectionProvider.java common/src/main/java/com/redhat/thermostat/common/storage/ConnectionFailedException.java common/src/test/java/com/redhat/thermostat/cli/AppContextSetupImplTest.java common/src/test/java/com/redhat/thermostat/common/dao/ConnectionTest.java common/src/test/java/com/redhat/thermostat/common/dao/MongoConnectionTest.java
diffstat 127 files changed, 3983 insertions(+), 1201 deletions(-) [+]
line wrap: on
line diff
--- a/agent/src/main/java/com/redhat/thermostat/agent/Agent.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/agent/src/main/java/com/redhat/thermostat/agent/Agent.java	Mon Apr 16 15:41:08 2012 +0200
@@ -44,6 +44,7 @@
 import com.redhat.thermostat.backend.Backend;
 import com.redhat.thermostat.backend.BackendRegistry;
 import com.redhat.thermostat.common.LaunchException;
+import com.redhat.thermostat.common.dao.DAOFactory;
 import com.redhat.thermostat.common.storage.AgentInformation;
 import com.redhat.thermostat.common.storage.BackendInformation;
 import com.redhat.thermostat.common.storage.Storage;
@@ -60,18 +61,21 @@
     private final BackendRegistry backendRegistry;
     private final AgentStartupConfiguration config;
 
+    private AgentInformation agentInfo;
+    
     private Storage storage;
     private Thread configWatcherThread = null;
 
-    public Agent(BackendRegistry backendRegistry, AgentStartupConfiguration config, Storage storage) {
-        this(backendRegistry, UUID.randomUUID(), config, storage);
+    public Agent(BackendRegistry backendRegistry, AgentStartupConfiguration config, DAOFactory daos) {
+        this(backendRegistry, UUID.randomUUID(), config, daos);
     }
 
-    public Agent(BackendRegistry registry, UUID agentId, AgentStartupConfiguration config, Storage storage) {
+    public Agent(BackendRegistry registry, UUID agentId, AgentStartupConfiguration config, DAOFactory daos) {
         this.id = agentId;
         this.backendRegistry = registry;
         this.config = config;
-        this.storage = storage;
+        this.storage = daos.getStorage();
+        this.storage.setAgentId(agentId);
     }
 
     private void startBackends() throws LaunchException {
@@ -99,7 +103,8 @@
     public synchronized void start() throws LaunchException {
         if (configWatcherThread == null) {
             startBackends();
-            storage.addAgentInformation(createAgentInformation());
+            agentInfo = createAgentInformation();
+            storage.addAgentInformation(agentInfo);
             configWatcherThread = new Thread(new ConfigurationWatcher(storage, backendRegistry), "Configuration Watcher");
             configWatcherThread.start();
         } else {
@@ -117,6 +122,7 @@
             backendInfo.setObserveNewJvm(backend.getObserveNewJvm());
             agentInfo.addBackend(backendInfo);
         }
+        agentInfo.setAlive(true);
         return agentInfo;
     }
 
@@ -131,12 +137,19 @@
                 }
             }
             configWatcherThread = null;
-            storage.removeAgentInformation();
+
             stopBackends();
-            // TODO
-//            if (config.getLocalMode()) {
-//                storage.purge();
-//            }
+            if (config.purge()) {
+                System.out.println("purging database");
+                logger.info("purging database");
+                storage.removeAgentInformation();
+                storage.purge();
+            } else {
+                agentInfo.setStopTime(System.currentTimeMillis());
+                agentInfo.setAlive(false);
+                storage.updateAgentInformation(agentInfo);
+            }
+            
         } else {
             logger.warning("Attempt to stop agent which is not active");
         }
--- a/agent/src/main/java/com/redhat/thermostat/agent/AgentApplication.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/agent/src/main/java/com/redhat/thermostat/agent/AgentApplication.java	Mon Apr 16 15:41:08 2012 +0200
@@ -51,18 +51,23 @@
 import com.redhat.thermostat.cli.CommandException;
 import com.redhat.thermostat.common.Constants;
 import com.redhat.thermostat.common.LaunchException;
+import com.redhat.thermostat.common.ThreadPoolTimerFactory;
+import com.redhat.thermostat.common.TimerFactory;
+import com.redhat.thermostat.common.appctx.ApplicationContext;
 import com.redhat.thermostat.common.config.InvalidConfigurationException;
-import com.redhat.thermostat.common.dao.Connection;
-import com.redhat.thermostat.common.dao.ConnectionProvider;
-import com.redhat.thermostat.common.dao.MongoConnectionProvider;
-import com.redhat.thermostat.common.storage.ConnectionFailedException;
-import com.redhat.thermostat.common.storage.MongoStorage;
-import com.redhat.thermostat.common.storage.Storage;
+import com.redhat.thermostat.common.dao.DAOFactory;
+import com.redhat.thermostat.common.dao.MongoDAOFactory;
+import com.redhat.thermostat.common.storage.Connection;
+import com.redhat.thermostat.common.storage.StorageProvider;
+import com.redhat.thermostat.common.storage.MongoStorageProvider;
+import com.redhat.thermostat.common.storage.Connection.ConnectionListener;
+import com.redhat.thermostat.common.storage.Connection.ConnectionStatus;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.tools.BasicCommand;
 
 public final class AgentApplication extends BasicCommand {
 
+    private CommandContext contex;
     private static final String NAME = "agent";
 
     // TODO: Use LocaleResources for i18n-ized strings.
@@ -96,30 +101,50 @@
         }
         
         LoggingUtils.setGlobalLogLevel(configuration.getLogLevel());
-        Logger logger = LoggingUtils.getLogger(AgentApplication.class);
+        final Logger logger = LoggingUtils.getLogger(AgentApplication.class);
 
-        ConnectionProvider connProv = new MongoConnectionProvider(configuration);
-        Connection connection = connProv.createConnection();
+        StorageProvider connProv = new MongoStorageProvider(configuration);
+        DAOFactory daoFactory = new MongoDAOFactory(connProv);
+        ApplicationContext.getInstance().setDAOFactory(daoFactory);
+        TimerFactory timerFactory = new ThreadPoolTimerFactory(1);
+        ApplicationContext.getInstance().setTimerFactory(timerFactory);
 
-        Storage storage = new MongoStorage(connection);
-        try {
-            storage.connect();
-            logger.fine("Storage configured with database URI.");
-        } catch (ConnectionFailedException ex) {
-            logger.log(Level.SEVERE, "Could not initialize storage layer.", ex);
-            System.exit(Constants.EXIT_UNABLE_TO_CONNECT_TO_DATABASE);
-        }
+        Connection connection = daoFactory.getConnection();
+        ConnectionListener connectionListener = new ConnectionListener() {
+            @Override
+            public void changed(ConnectionStatus newStatus) {
+                switch (newStatus) {
+                case DISCONNECTED:
+                    logger.warning("Unexpected disconnect event.");
+                    break;
+                case CONNECTING:
+                    logger.fine("Connecting to storage.");
+                    break;
+                case CONNECTED:
+                    logger.fine("Connected to storage.");
+                    break;
+                case FAILED_TO_CONNECT:
+                    logger.warning("Could not connect to storage.");
+                    System.exit(Constants.EXIT_UNABLE_TO_CONNECT_TO_DATABASE);
+                default:
+                    logger.warning("Unfamiliar ConnectionStatus value");
+                }
+            }
+        };
+
+        connection.addListener(connectionListener);
+        connection.connect();
+        logger.fine("Connecting to storage...");
 
         BackendRegistry backendRegistry = null;
         try {
-            backendRegistry = new BackendRegistry(configuration, storage);
+            backendRegistry = new BackendRegistry(configuration);
         } catch (BackendLoadException ble) {
             logger.log(Level.SEVERE, "Could not get BackendRegistry instance.", ble);
             System.exit(Constants.EXIT_BACKEND_LOAD_ERROR);
         }
 
-        Agent agent = new Agent(backendRegistry, configuration, storage);
-        storage.setAgentId(agent.getId());
+        Agent agent = new Agent(backendRegistry, configuration, daoFactory);
         try {
             logger.fine("Starting agent.");
             agent.start();
@@ -131,6 +156,9 @@
         }
         logger.fine("Agent started.");
 
+        contex.getConsole().getOutput().println("Agent id: " + agent.getId());
+        logger.fine("Agent id: " + agent.getId());
+        
         try {
             System.in.read();
         } catch (IOException e) {
@@ -144,6 +172,7 @@
     @Override
     public void run(CommandContext ctx) throws CommandException {
         try {
+            contex = ctx;
             parseArguments(Arrays.asList(ctx.getArguments()));
             if (!parser.isHelp()) {
                 runAgent();
--- a/agent/src/main/java/com/redhat/thermostat/agent/config/AgentConfigsUtils.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/agent/src/main/java/com/redhat/thermostat/agent/config/AgentConfigsUtils.java	Mon Apr 16 15:41:08 2012 +0200
@@ -89,6 +89,12 @@
             String db = properties.getProperty(AgentProperties.DB_URL.name());
             configuration.setDatabaseURL(db);
         }
+        
+        configuration.setPurge(true);
+        if (properties.containsKey(AgentProperties.SAVE_ON_EXIT.name())) {
+            String purge = (String) properties.get(AgentProperties.SAVE_ON_EXIT.name());
+            configuration.setPurge(!Boolean.parseBoolean(purge));
+        }
     }
     
     public static Level getLogLevel(String logLevel) {
--- a/agent/src/main/java/com/redhat/thermostat/agent/config/AgentOptionParser.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/agent/src/main/java/com/redhat/thermostat/agent/config/AgentOptionParser.java	Mon Apr 16 15:41:08 2012 +0200
@@ -67,6 +67,7 @@
 
         parser.accepts(Args.DEBUG.option, Args.DEBUG.description);
         parser.accepts(Args.HELP.option, Args.HELP.description);
+        parser.accepts(Args.SAVE_ON_EXIT.option, Args.SAVE_ON_EXIT.description);
         
         OptionSpec<String> logLevel =
                 parser.accepts(Args.LEVEL.option, Args.LEVEL.description).
@@ -82,6 +83,10 @@
             return;
         }
         
+        if (options.has(Args.SAVE_ON_EXIT.option)) {
+            configuration.setPurge(false);
+        }
+        
         if (options.has(Args.LEVEL.option)) {
             String levelString = logLevel.value(options);
             Level level = AgentConfigsUtils.getLogLevel(levelString);
@@ -119,6 +124,7 @@
         
         // TODO: localize
         LEVEL("logLevel", "log level"),
+        SAVE_ON_EXIT("saveOnExit", "save the data on exit"),
         DB("dbUrl", "connect to the given url"),
         DEBUG("debug", "launch with debug console enabled"),
         HELP("help", "print this help and exit");
--- a/agent/src/main/java/com/redhat/thermostat/agent/config/AgentProperties.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/agent/src/main/java/com/redhat/thermostat/agent/config/AgentProperties.java	Mon Apr 16 15:41:08 2012 +0200
@@ -42,5 +42,6 @@
     BACKENDS,
     LOG_LEVEL,
     DEBUG_CONSOLE,
-    DB_URL
+    DB_URL,
+    SAVE_ON_EXIT
 }
--- a/agent/src/main/java/com/redhat/thermostat/agent/config/AgentStartupConfiguration.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/agent/src/main/java/com/redhat/thermostat/agent/config/AgentStartupConfiguration.java	Mon Apr 16 15:41:08 2012 +0200
@@ -38,9 +38,7 @@
 
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Properties;
@@ -58,6 +56,7 @@
     
     private Level logLevel;
     private boolean debugConsole;
+    private boolean purge;
     
     private String url;
     
@@ -135,4 +134,12 @@
     public long getStartTime() {
         return startTime;
     }
+
+    void setPurge(boolean purge) {
+        this.purge = purge;
+    }
+    
+    public boolean purge() {
+        return purge;
+    }
 }
--- a/agent/src/main/java/com/redhat/thermostat/backend/Backend.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/agent/src/main/java/com/redhat/thermostat/backend/Backend.java	Mon Apr 16 15:41:08 2012 +0200
@@ -42,8 +42,8 @@
 import java.util.Map.Entry;
 
 import com.redhat.thermostat.common.LaunchException;
+import com.redhat.thermostat.common.dao.DAOFactory;
 import com.redhat.thermostat.common.storage.Category;
-import com.redhat.thermostat.common.storage.Chunk;
 import com.redhat.thermostat.common.storage.Storage;
 
 /**
@@ -53,6 +53,7 @@
 public abstract class Backend {
 
     private boolean initialConfigurationComplete = false;
+    protected DAOFactory df = null;
     private Storage storage = null;
     private boolean observeNewJvm = attachToNewProcessByDefault();
 
@@ -61,7 +62,7 @@
     private String description;
     
     private BackendID id;
-    
+
     /**
      * 
      * @param configMap a map containing the settings that this backend has been configured with.
@@ -69,7 +70,7 @@
      */
     protected final void setInitialConfiguration(Map<String, String> configMap) throws BackendLoadException {
         if (initialConfigurationComplete) {
-            throw new BackendLoadException("A backend may only receive intitial configuration once.");
+            throw new BackendLoadException("A backend may only receive initial configuration once.");
         }
         for (Entry<String, String> e : configMap.entrySet()) {
             String key = e.getKey();
@@ -84,13 +85,17 @@
         initialConfigurationComplete = true;
     }
 
-    public final void setStorage(Storage storage) {
-        this.storage = storage;
+    public final void setDAOFactory(DAOFactory df) {
+        this.df = df;
+        this.storage = df.getStorage();
         for (Category cat : getCategories()) {
             storage.registerCategory(cat);
         }
+        setDAOFactoryAction();
     }
 
+    protected abstract void setDAOFactoryAction();
+
     protected abstract Collection<Category> getCategories();
 
     /**
@@ -228,14 +233,6 @@
         observeNewJvm = newValue;
     }
 
-    public void store(Chunk chunk) {
-        storage.putChunk(chunk);
-    }
-
-    public void update(Chunk chunk) {
-        storage.updateChunk(chunk);
-    }
-
     void setID(BackendID backendID) {
         this.id = backendID;
     }
--- a/agent/src/main/java/com/redhat/thermostat/backend/BackendRegistry.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/agent/src/main/java/com/redhat/thermostat/backend/BackendRegistry.java	Mon Apr 16 15:41:08 2012 +0200
@@ -45,7 +45,8 @@
 import java.util.logging.Logger;
 
 import com.redhat.thermostat.agent.config.AgentStartupConfiguration;
-import com.redhat.thermostat.common.storage.Storage;
+import com.redhat.thermostat.common.appctx.ApplicationContext;
+import com.redhat.thermostat.common.dao.DAOFactory;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 
 /**
@@ -58,14 +59,17 @@
 
     private final Map<String, Backend> registeredBackends;
 
-    public BackendRegistry(AgentStartupConfiguration config, Storage storage) throws BackendLoadException {
-        this(config, new BackendConfigurationLoader(), storage);
+    public BackendRegistry(AgentStartupConfiguration config) throws BackendLoadException {
+        this(config, new BackendConfigurationLoader());
     }
 
-    public BackendRegistry(AgentStartupConfiguration config, BackendConfigurationLoader backendConfigLoader, Storage storage) throws BackendLoadException {
+    public BackendRegistry(AgentStartupConfiguration config, BackendConfigurationLoader backendConfigLoader) throws BackendLoadException {
+
         registeredBackends = new HashMap<String, Backend>();
         
         List<BackendID> backends = config.getBackends();
+
+        DAOFactory df = ApplicationContext.getInstance().getDAOFactory();
         
         /*
          * Configure the dynamic/custom backends
@@ -78,11 +82,11 @@
                 Class<? extends Backend> narrowed = c.asSubclass(Backend.class);
                 Constructor<? extends Backend> backendConstructor = narrowed.getConstructor();
                 backend = backendConstructor.newInstance();
-                
+
+                backend.setDAOFactory(df);
                 backend.setID(backendID);
                 
                 backend.setInitialConfiguration(backendConfigLoader.retrieveBackendConfigs(backend.getName()));
-                backend.setStorage(storage);
             } catch (Exception e) {
                 throw new BackendLoadException("Could not instantiate configured backend class: " + backendID.getClassName(), e);
             }
--- a/agent/src/main/java/com/redhat/thermostat/backend/sample/SampleBackend.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/agent/src/main/java/com/redhat/thermostat/backend/sample/SampleBackend.java	Mon Apr 16 15:41:08 2012 +0200
@@ -130,4 +130,10 @@
         return false;
     }
 
+    @Override
+    protected void setDAOFactoryAction() {
+        // TODO Auto-generated method stub
+        
+    }
+
 }
--- a/agent/src/main/java/com/redhat/thermostat/backend/system/JvmStatHostListener.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/agent/src/main/java/com/redhat/thermostat/backend/system/JvmStatHostListener.java	Mon Apr 16 15:41:08 2012 +0200
@@ -56,24 +56,25 @@
 
 import com.redhat.thermostat.agent.JvmStatusListener;
 import com.redhat.thermostat.agent.JvmStatusNotifier;
-import com.redhat.thermostat.common.dao.VmInfoConverter;
+import com.redhat.thermostat.common.dao.DAOFactory;
 import com.redhat.thermostat.common.dao.VmInfoDAO;
 import com.redhat.thermostat.common.model.VmInfo;
-import com.redhat.thermostat.common.storage.Chunk;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 
 public class JvmStatHostListener implements HostListener, JvmStatusNotifier {
 
     private static final Logger logger = LoggingUtils.getLogger(JvmStatHostListener.class);
 
-    private SystemBackend backend;
-
-    private Map<Integer, JvmStatVmListener> listenerMap = new HashMap<Integer, JvmStatVmListener>();
+    private boolean attachNew;
+    private final DAOFactory df;
+    private final VmInfoDAO vmInfoDAO;
 
     private Set<JvmStatusListener> statusListeners = new CopyOnWriteArraySet<JvmStatusListener>();
 
-    public void setBackend(SystemBackend backend) {
-        this.backend = backend;
+    JvmStatHostListener(DAOFactory df, boolean attachNew) {
+        this.df = df;
+        this.vmInfoDAO = df.getVmInfoDAO();
+        this.attachNew = attachNew;
     }
 
     @Override
@@ -129,17 +130,15 @@
                         extractor.getMainClass(), extractor.getCommandLine(),
                         extractor.getVmName(), extractor.getVmInfo(), extractor.getVmVersion(), extractor.getVmArguments(),
                         properties, environment, loadedNativeLibraries);
-                backend.store(new VmInfoConverter().toChunk(info));
+                vmInfoDAO.putVmInfo(info);
                 logger.finer("Sent VM_STARTED messsage");
             } catch (MonitorException me) {
                 logger.log(Level.WARNING, "error getting vm info for " + vmId, me);
             }
 
-            if (backend.getObserveNewJvm()) {
-                JvmStatVmListener listener = new JvmStatVmListener(backend, vmId);
-                listenerMap.put(vmId, listener);
-                vm.addVmListener(listener);
-                vm.addVmListener(new JvmStatVmClassListener(backend, vmId));
+            if (attachNew) {
+                vm.addVmListener(new JvmStatVmListener(df, vmId));
+                vm.addVmListener(new JvmStatVmClassListener(df, vmId));
             } else {
                 logger.log(Level.FINE, "skipping new vm " + vmId);
             }
@@ -155,22 +154,13 @@
                 new VmIdentifier(vmId.toString()));
         if (resolvedVmID != null) {
             long stopTime = System.currentTimeMillis();
-            listenerMap.remove(vmId);
             for (JvmStatusListener statusListener : statusListeners) {
                 statusListener.jvmStopped(vmId);
             }
-            backend.update(makeVmInfoUpdateStoppedChunk(vmId, stopTime));
+            vmInfoDAO.putVmStoppedTime(vmId, stopTime);
         }
     }
 
-    private Chunk makeVmInfoUpdateStoppedChunk(int vmId, long stopTimeStamp) {
-        // FIXME later
-        Chunk chunk = new Chunk(VmInfoDAO.vmInfoCategory, false);
-        chunk.put(VmInfoDAO.vmIdKey, vmId);
-        chunk.put(VmInfoDAO.stopTimeKey, stopTimeStamp);
-        return chunk;
-    }
-
     @Override
     public void addJvmStatusListener(JvmStatusListener listener) {
         statusListeners.add(listener);
--- a/agent/src/main/java/com/redhat/thermostat/backend/system/JvmStatVmClassListener.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/agent/src/main/java/com/redhat/thermostat/backend/system/JvmStatVmClassListener.java	Mon Apr 16 15:41:08 2012 +0200
@@ -45,7 +45,8 @@
 import sun.jvmstat.monitor.event.VmEvent;
 import sun.jvmstat.monitor.event.VmListener;
 
-import com.redhat.thermostat.common.dao.VmClassStatConverter;
+import com.redhat.thermostat.common.dao.DAOFactory;
+import com.redhat.thermostat.common.dao.VmClassStatDAO;
 import com.redhat.thermostat.common.model.VmClassStat;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 
@@ -53,11 +54,11 @@
 
     private static final Logger logger = LoggingUtils.getLogger(JvmStatVmClassListener.class);
 
-    private SystemBackend backend;
+    private VmClassStatDAO dao;
     private int vmId;
 
-    JvmStatVmClassListener(SystemBackend backend, int vmId) {
-        this.backend = backend;
+    JvmStatVmClassListener(DAOFactory df, int vmId) {
+        this.dao = df.getVmClassStatsDAO();
         this.vmId = vmId;
     }
 
@@ -79,8 +80,7 @@
             long loadedClasses = extractor.getLoadedClasses();
             long timestamp = System.currentTimeMillis();
             VmClassStat stat = new VmClassStat(vmId, timestamp, loadedClasses);
-            VmClassStatConverter dao = new VmClassStatConverter();
-            backend.store(dao.toChunk(stat));
+            dao.putVmClassStat(stat);
         } catch (MonitorException e) {
             logger.log(Level.WARNING, "error gathering class info for vm " + vmId, e);
         }
--- a/agent/src/main/java/com/redhat/thermostat/backend/system/JvmStatVmListener.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/agent/src/main/java/com/redhat/thermostat/backend/system/JvmStatVmListener.java	Mon Apr 16 15:41:08 2012 +0200
@@ -47,8 +47,9 @@
 import sun.jvmstat.monitor.event.VmEvent;
 import sun.jvmstat.monitor.event.VmListener;
 
-import com.redhat.thermostat.common.dao.VmGcStatConverter;
-import com.redhat.thermostat.common.dao.VmMemoryStatConverter;
+import com.redhat.thermostat.common.dao.DAOFactory;
+import com.redhat.thermostat.common.dao.VmGcStatDAO;
+import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.model.VmGcStat;
 import com.redhat.thermostat.common.model.VmMemoryStat;
 import com.redhat.thermostat.common.model.VmMemoryStat.Generation;
@@ -60,10 +61,12 @@
     private static final Logger logger = LoggingUtils.getLogger(JvmStatVmListener.class);
 
     private final int vmId;
-    private final SystemBackend backend;
+    private final VmGcStatDAO gcDAO;
+    private final VmMemoryStatDAO memDAO;
 
-    public JvmStatVmListener(SystemBackend backend, int vmId) {
-        this.backend = backend;
+    public JvmStatVmListener(DAOFactory df, int vmId) {
+        gcDAO = df.getVmGcStatDAO();
+        memDAO = df.getVmMemoryStatDAO();
         this.vmId = vmId;
     }
 
@@ -97,7 +100,7 @@
                         extractor.getCollectorName(i),
                         extractor.getCollectorInvocations(i),
                         extractor.getCollectorTime(i));
-                backend.store(new VmGcStatConverter().toChunk(stat));
+                gcDAO.putVmGcStat(stat);
             }
         } catch (MonitorException e) {
             logger.log(Level.WARNING, "error gathering gc info for vm " + vmId, e);
@@ -132,7 +135,7 @@
                     s.used = extractor.getSpaceUsed(generation, space);
                 }
             }
-            backend.store(new VmMemoryStatConverter().toChunk(stat));
+            memDAO.putVmMemoryStat(stat);
         } catch (MonitorException e) {
             logger.log(Level.WARNING, "error gathering memory info for vm " + vmId, e);
         }
--- a/agent/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/agent/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java	Mon Apr 16 15:41:08 2012 +0200
@@ -57,16 +57,11 @@
 import com.redhat.thermostat.backend.Backend;
 import com.redhat.thermostat.common.Clock;
 import com.redhat.thermostat.common.SystemClock;
-import com.redhat.thermostat.common.dao.CpuStatConverter;
 import com.redhat.thermostat.common.dao.CpuStatDAO;
-import com.redhat.thermostat.common.dao.HostInfoConverter;
 import com.redhat.thermostat.common.dao.HostInfoDAO;
-import com.redhat.thermostat.common.dao.MemoryStatConverter;
 import com.redhat.thermostat.common.dao.MemoryStatDAO;
-import com.redhat.thermostat.common.dao.NetworkInterfaceInfoConverter;
 import com.redhat.thermostat.common.dao.NetworkInterfaceInfoDAO;
 import com.redhat.thermostat.common.dao.VmClassStatDAO;
-import com.redhat.thermostat.common.dao.VmCpuStatConverter;
 import com.redhat.thermostat.common.dao.VmCpuStatDAO;
 import com.redhat.thermostat.common.dao.VmGcStatDAO;
 import com.redhat.thermostat.common.dao.VmInfoDAO;
@@ -79,6 +74,12 @@
 
     private static final Logger logger = LoggingUtils.getLogger(SystemBackend.class);
 
+    private CpuStatDAO cpuStats;
+    private HostInfoDAO hostInfos;
+    private MemoryStatDAO memoryStats;
+    private VmCpuStatDAO vmCpuStats;
+    private NetworkInterfaceInfoDAO networkInterfaces;
+
     private final List<Category> categories = new ArrayList<Category>();
 
     private final Set<Integer> pidsToMonitor = new CopyOnWriteArraySet<Integer>();
@@ -89,13 +90,17 @@
 
     private HostIdentifier hostId = null;
     private MonitoredHost host = null;
-    private JvmStatHostListener hostListener = new JvmStatHostListener();
+    private JvmStatHostListener hostListener;
 
     private final VmCpuStatBuilder vmCpuBuilder;
+    private final HostInfoBuilder hostInfoBuilder;
+    private final CpuStatBuilder cpuStatBuilder;
+    private final MemoryStatBuilder memoryStatBuilder;
 
     public SystemBackend() {
+        super();
+
         // Set up categories that will later be registered.
-
         categories.add(CpuStatDAO.cpuStatCategory);
         categories.add(HostInfoDAO.hostInfoCategory);
         categories.add(MemoryStatDAO.memoryStatCategory);
@@ -110,6 +115,20 @@
         ProcessStatusInfoBuilder builder = new ProcessStatusInfoBuilder(new ProcDataSource());
         long ticksPerSecond = SysConf.getClockTicksPerSecond();
         vmCpuBuilder = new VmCpuStatBuilder(clock, ticksPerSecond, builder);
+        ProcDataSource source = new ProcDataSource();
+        hostInfoBuilder = new HostInfoBuilder(source);
+        cpuStatBuilder = new CpuStatBuilder(source);
+        memoryStatBuilder = new MemoryStatBuilder(source);
+    }
+
+    @Override
+    protected void setDAOFactoryAction() {
+        cpuStats = df.getCpuStatDAO();
+        hostInfos = df.getHostInfoDAO();
+        memoryStats = df.getMemoryStatDAO();
+        vmCpuStats = df.getVmCpuStatDAO();
+        networkInterfaces = df.getNetworkInterfaceInfoDAO();
+        hostListener = new JvmStatHostListener(df, getObserveNewJvm());
     }
 
     @Override
@@ -117,28 +136,30 @@
         if (timer != null) {
             return true;
         }
+        if (df == null) {
+            throw new IllegalStateException("Cannot activate backend without DAOFactory.");
+        }
 
         addJvmStatusListener(this);
 
         if (!getObserveNewJvm()) {
             logger.fine("not monitoring new vms");
         }
-        store(new HostInfoConverter().toChunk(new HostInfoBuilder(new ProcDataSource()).build()));
+        hostInfos.putHostInfo(hostInfoBuilder.build());
 
         timer = new Timer();
         timer.scheduleAtFixedRate(new TimerTask() {
             @Override
             public void run() {
-                ProcDataSource dataSource = new ProcDataSource();
-                store(new CpuStatConverter().toChunk(new CpuStatBuilder(dataSource).build()));
+                cpuStats.putCpuStat(cpuStatBuilder.build());
                 for (NetworkInterfaceInfo info: NetworkInfoBuilder.build()) {
-                    store(new NetworkInterfaceInfoConverter().toChunk(info));
+                    networkInterfaces.putNetworkInterfaceInfo(info);
                 }
-                store(new MemoryStatConverter().toChunk(new MemoryStatBuilder(dataSource).build()));
+                memoryStats.putMemoryStat(memoryStatBuilder.build());
 
                 for (Integer pid : pidsToMonitor) {
                     if (vmCpuBuilder.knowsAbout(pid)) {
-                        store(new VmCpuStatConverter().toChunk(vmCpuBuilder.build(pid)));
+                        vmCpuStats.putVmCpuStat(vmCpuBuilder.build(pid));
                     } else {
                         vmCpuBuilder.learnAbout(pid);
                     }
@@ -149,7 +170,6 @@
         try {
             hostId = new HostIdentifier((String) null);
             host = MonitoredHost.getMonitoredHost(hostId);
-            hostListener.setBackend(this);
             host.addHostListener(hostListener);
         } catch (MonitorException me) {
             logger.log(Level.WARNING, "problems with connecting jvmstat to local machine", me);
--- a/agent/src/test/java/com/redhat/thermostat/TestUtils.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/agent/src/test/java/com/redhat/thermostat/TestUtils.java	Mon Apr 16 15:41:08 2012 +0200
@@ -43,10 +43,6 @@
 import java.util.Properties;
 import java.util.Random;
 
-import junit.framework.Assert;
-
-import org.junit.BeforeClass;
-
 import com.redhat.thermostat.agent.config.AgentProperties;
 import com.redhat.thermostat.backend.BackendsProperties;
 
@@ -85,6 +81,7 @@
         Properties props = new Properties();            
 
         props.setProperty(AgentProperties.BACKENDS.name(), "system");
+        props.setProperty(AgentProperties.SAVE_ON_EXIT.name(), "false");
         props.setProperty(AgentProperties.LOG_LEVEL.name(), "WARNING");
 
         props.store(new FileOutputStream(tmpConfigs), "thermostat agent test properties");
--- a/agent/src/test/java/com/redhat/thermostat/agent/AgentTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/agent/src/test/java/com/redhat/thermostat/agent/AgentTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -39,45 +39,61 @@
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.when;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
 import com.redhat.thermostat.agent.config.AgentStartupConfiguration;
 import com.redhat.thermostat.backend.Backend;
 import com.redhat.thermostat.backend.BackendRegistry;
+import com.redhat.thermostat.common.dao.DAOFactory;
 import com.redhat.thermostat.common.storage.AgentInformation;
 import com.redhat.thermostat.common.storage.BackendInformation;
 import com.redhat.thermostat.common.storage.Storage;
 
 public class AgentTest {
 
-    @Test
-    public void testStartAgent() throws Exception {
-        // Setup class under test and test data (config, backendRegistry).
-        AgentStartupConfiguration config = mock(AgentStartupConfiguration.class);
+    private AgentStartupConfiguration config;
+    private Storage storage;
+    private BackendRegistry backendRegistry;
+    private Backend backend;
+    private DAOFactory daos;
+    
+    @Before
+    public void setUp() {
+        config = mock(AgentStartupConfiguration.class);
         when(config.getStartTime()).thenReturn(123L);
-
-        Storage storage = mock(Storage.class);
-
-        Backend backend = mock(Backend.class);
+        when(config.purge()).thenReturn(true);
+        
+        storage = mock(Storage.class);
+        daos = mock(DAOFactory.class);
+        when(daos.getStorage()).thenReturn(storage);
+        
+        backend = mock(Backend.class);
         when(backend.getName()).thenReturn("testname");
         when(backend.getDescription()).thenReturn("testdesc");
         when(backend.getObserveNewJvm()).thenReturn(true);
         when(backend.activate()).thenReturn(true); // TODO: activate() should not return anything and throw exception in error case.
         Collection<Backend> backends = new ArrayList<Backend>();
         backends.add(backend);
-
-        BackendRegistry backendRegistry = mock(BackendRegistry.class);
+        
+        backendRegistry = mock(BackendRegistry.class);
         when(backendRegistry.getAll()).thenReturn(backends);
-
+    }
+    
+    @Test
+    public void testStartAgent() throws Exception {
+        
         // Start agent.
-        Agent agent = new Agent(backendRegistry, config, storage);
+        Agent agent = new Agent(backendRegistry, config, daos);
         agent.start();
 
         // Verify that backend has been activated and storage received the agent information.
@@ -94,4 +110,39 @@
         // TODO: We should probably also test getPIDs() and getConfiguration(), but it's not clear to me at this point
         // what those should really do (and it looks like they're not implemented yet).
     }
+    
+    @Test
+    public void testStopAgentWithPurging() throws Exception {
+        Agent agent = new Agent(backendRegistry, config, daos);
+        agent.start();
+        
+        // stop agent
+        agent.stop();
+        
+        verify(backend).deactivate();
+
+        ArgumentCaptor<AgentInformation> argument = ArgumentCaptor.forClass(AgentInformation.class);        
+        verify(storage, never()).updateAgentInformation(argument.capture());
+        verify(storage, times(1)).purge();
+    }
+    
+    @Test
+    public void testStopAgent() throws Exception {
+        
+        AgentStartupConfiguration config = mock(AgentStartupConfiguration.class);
+        when(config.getStartTime()).thenReturn(123L);
+        when(config.purge()).thenReturn(false);
+        
+        Agent agent = new Agent(backendRegistry, config, daos);
+        agent.start();
+        
+        // stop agent
+        agent.stop();
+        
+        verify(backend).deactivate();
+
+        ArgumentCaptor<AgentInformation> argument = ArgumentCaptor.forClass(AgentInformation.class);        
+        verify(storage).updateAgentInformation(argument.capture());
+        verify(storage, times(0)).purge();
+    }
 }
--- a/agent/src/test/java/com/redhat/thermostat/agent/config/AgentOptionParserTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/agent/src/test/java/com/redhat/thermostat/agent/config/AgentOptionParserTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -37,11 +37,9 @@
 package com.redhat.thermostat.agent.config;
 
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Properties;
 import java.util.logging.Level;
 
 import junit.framework.Assert;
@@ -77,13 +75,14 @@
         args.add("testURL");
         args.add("--debug");
         
-        AgentStartupConfiguration configs = new AgentStartupConfiguration();
+        AgentStartupConfiguration configs = AgentConfigsUtils.createAgentConfigs();
         AgentOptionParser parser = new AgentOptionParser(configs, args);
         parser.parse();
         
         Assert.assertEquals("testURL", configs.getDBConnectionString());
         Assert.assertEquals(Level.ALL, configs.getLogLevel());
         Assert.assertTrue(configs.isDebugConsole());
+        Assert.assertTrue(configs.purge());
     }
     
     @Test
@@ -94,6 +93,7 @@
         args.add("FINE");
         args.add("--dbUrl");
         args.add("testURL2");
+        args.add("--saveOnExit");
         
         AgentStartupConfiguration configs = new AgentStartupConfiguration();
         AgentOptionParser parser = new AgentOptionParser(configs, args);
@@ -102,5 +102,6 @@
         Assert.assertEquals("testURL2", configs.getDBConnectionString());
         Assert.assertEquals(Level.FINE, configs.getLogLevel());
         Assert.assertFalse(configs.isDebugConsole());
+        Assert.assertFalse(configs.purge());
     }
 }
--- a/agent/src/test/java/com/redhat/thermostat/backend/BackendRegistryTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/agent/src/test/java/com/redhat/thermostat/backend/BackendRegistryTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -53,13 +53,19 @@
 import org.junit.Test;
 
 import com.redhat.thermostat.agent.config.AgentStartupConfiguration;
+import com.redhat.thermostat.common.appctx.ApplicationContext;
 import com.redhat.thermostat.common.config.InvalidConfigurationException;
+import com.redhat.thermostat.common.dao.DAOFactory;
 import com.redhat.thermostat.common.storage.Category;
 import com.redhat.thermostat.common.storage.Storage;
 
 public class BackendRegistryTest {
 
     public static class MockBackend extends Backend {
+        public MockBackend() {
+            super();
+        }
+
         @Override
         protected Collection<Category> getCategories() {
             return Collections.emptyList();
@@ -84,23 +90,32 @@
         public boolean attachToNewProcessByDefault() {
             return false;
         }
+
+        @Override
+        protected void setDAOFactoryAction() {
+            // TODO Auto-generated method stub
+            
+        }
     }
 
     private List<BackendID> backends;
     private AgentStartupConfiguration config;
     private BackendConfigurationLoader configLoader;
-    private Storage storage;
 
     @Before
     public void setUp() throws InvalidConfigurationException {
         backends = new ArrayList<>();
 
-        storage = mock(Storage.class);
         config = mock(AgentStartupConfiguration.class);
         when(config.getBackends()).thenReturn(backends);
 
         configLoader = mock(BackendConfigurationLoader.class);
         when(configLoader.retrieveBackendConfigs(any(String.class))).thenReturn(new HashMap<String,String>());
+
+        Storage storage = mock(Storage.class);
+        DAOFactory df = mock(DAOFactory.class);
+        when(df.getStorage()).thenReturn(storage);
+        ApplicationContext.getInstance().setDAOFactory(df);
     }
 
     @After
@@ -108,7 +123,6 @@
         backends = null;
         config = null;
         configLoader = null;
-        storage = null;
     }
 
     @Test
@@ -116,14 +130,14 @@
         /* setup fake backend */
         backends.add(new BackendID("mock", MockBackend.class.getName()));
 
-        BackendRegistry registry = new BackendRegistry(config, configLoader, storage);
+        BackendRegistry registry = new BackendRegistry(config, configLoader);
         assertEquals(1, registry.getAll().size());
         assertNotNull(registry.getByName("mock"));
     }
 
     @Test
     public void testNoBackendsRegistered() throws InvalidConfigurationException, BackendLoadException {
-        BackendRegistry registry = new BackendRegistry(config, configLoader, storage);
+        BackendRegistry registry = new BackendRegistry(config, configLoader);
         assertEquals(0, registry.getAll().size());
         assertEquals(null, registry.getByName("system"));
         assertEquals(null, registry.getByName("mock"));
@@ -137,7 +151,7 @@
         backends.add(new BackendID("mock", MockBackend.class.getClass().getName()));
 
         /* load the backends */
-        new BackendRegistry(config, configLoader, storage);
+        new BackendRegistry(config, configLoader);
     }
 
 }
--- a/agent/src/test/java/com/redhat/thermostat/backend/system/JvmStatHostListenerTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/agent/src/test/java/com/redhat/thermostat/backend/system/JvmStatHostListenerTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -48,6 +48,10 @@
 import org.junit.Test;
 import org.mockito.Matchers;
 
+import com.redhat.thermostat.common.dao.DAOFactory;
+import com.redhat.thermostat.common.dao.VmClassStatDAO;
+import com.redhat.thermostat.common.dao.VmInfoDAO;
+
 import sun.jvmstat.monitor.HostIdentifier;
 import sun.jvmstat.monitor.MonitoredHost;
 import sun.jvmstat.monitor.MonitoredVm;
@@ -75,10 +79,14 @@
         when(host.getMonitoredVm(any(VmIdentifier.class))).thenReturn(vm);
         when(vmEvent.getMonitoredHost()).thenReturn(host);
 
-        JvmStatHostListener l = new JvmStatHostListener();
+        VmClassStatDAO vmClassDAO = mock(VmClassStatDAO.class);
+        VmInfoDAO vmInfoDAO = mock(VmInfoDAO.class);
+        DAOFactory df = mock(DAOFactory.class);
+        when(df.getVmClassStatsDAO()).thenReturn(vmClassDAO);
+        when(df.getVmInfoDAO()).thenReturn(vmInfoDAO);
+        JvmStatHostListener l = new JvmStatHostListener(df, true);
         SystemBackend backend = mock(SystemBackend.class);
         when(backend.getObserveNewJvm()).thenReturn(true);
-        l.setBackend(backend);
 
         l.vmStatusChanged(vmEvent);
 
--- a/agent/src/test/java/com/redhat/thermostat/backend/system/JvmStatVmClassListenerTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/agent/src/test/java/com/redhat/thermostat/backend/system/JvmStatVmClassListenerTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -48,42 +48,49 @@
 import sun.jvmstat.monitor.MonitoredVm;
 import sun.jvmstat.monitor.event.VmEvent;
 
-import com.redhat.thermostat.common.storage.Chunk;
-import com.redhat.thermostat.common.storage.Key;
+import com.redhat.thermostat.common.dao.DAOFactory;
+import com.redhat.thermostat.common.dao.VmClassStatDAO;
+import com.redhat.thermostat.common.model.VmClassStat;
 
 public class JvmStatVmClassListenerTest {
 
+    private static final Integer VM_ID = 123;
+    private static final Long LOADED_CLASSES = 1234L;
+
     @Test
     public void testMonitorUpdatedClassStat() throws Exception {
 
-        SystemBackend backend = mock(SystemBackend.class);
-        int vmId = 123;
-        JvmStatVmClassListener l = new JvmStatVmClassListener(backend, vmId);
+        VmClassStatDAO dao = mock(VmClassStatDAO.class);
+        DAOFactory df = mock(DAOFactory.class);
+        when(df.getVmClassStatsDAO()).thenReturn(dao);
+        JvmStatVmClassListener l = new JvmStatVmClassListener(df, VM_ID);
         VmEvent vmEvent = mock(VmEvent.class);
         MonitoredVm monitoredVm = mock(MonitoredVm.class);
         Monitor m = mock(Monitor.class);
-        when(m.getValue()).thenReturn(new Long(1234));
+        when(m.getValue()).thenReturn(LOADED_CLASSES);
         when(monitoredVm.findByName("java.cls.loadedClasses")).thenReturn(m);
         when(vmEvent.getMonitoredVm()).thenReturn(monitoredVm);
 
         l.monitorsUpdated(vmEvent);
 
-        ArgumentCaptor<Chunk> chunkArg = ArgumentCaptor.forClass(Chunk.class);
-        verify(backend).store(chunkArg.capture());
-        assertEquals((Long) 1234L, chunkArg.getValue().get(new Key<Long>("loadedClasses", false)));
-        assertEquals((Integer) 123, chunkArg.getValue().get(new Key<Integer>("vm-id", false)));
+        ArgumentCaptor<VmClassStat> arg = ArgumentCaptor.forClass(VmClassStat.class);
+        verify(dao).putVmClassStat(arg.capture());
+        VmClassStat stat = arg.getValue();
+        assertEquals(LOADED_CLASSES, (Long) stat.getLoadedClasses());
+        assertEquals(VM_ID, (Integer) stat.getVmId());
     }
 
     @Test
     public void testMonitorUpdatedClassStatTwice() throws Exception {
 
-        SystemBackend backend = mock(SystemBackend.class);
-        int vmId = 123;
-        JvmStatVmClassListener l = new JvmStatVmClassListener(backend, vmId);
+        VmClassStatDAO dao = mock(VmClassStatDAO.class);
+        DAOFactory df = mock(DAOFactory.class);
+        when(df.getVmClassStatsDAO()).thenReturn(dao);
+        JvmStatVmClassListener l = new JvmStatVmClassListener(df, VM_ID);
         VmEvent vmEvent = mock(VmEvent.class);
         MonitoredVm monitoredVm = mock(MonitoredVm.class);
         Monitor m = mock(Monitor.class);
-        when(m.getValue()).thenReturn(new Long(1234));
+        when(m.getValue()).thenReturn(LOADED_CLASSES);
         when(monitoredVm.findByName("java.cls.loadedClasses")).thenReturn(m);
         when(vmEvent.getMonitoredVm()).thenReturn(monitoredVm);
 
--- a/agent/src/test/java/com/redhat/thermostat/backend/system/SystemBackendTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/agent/src/test/java/com/redhat/thermostat/backend/system/SystemBackendTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -39,6 +39,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import java.util.Collection;
 
@@ -46,6 +47,7 @@
 import org.junit.Test;
 
 import com.redhat.thermostat.common.dao.CpuStatDAO;
+import com.redhat.thermostat.common.dao.DAOFactory;
 import com.redhat.thermostat.common.dao.HostInfoDAO;
 import com.redhat.thermostat.common.dao.MemoryStatDAO;
 import com.redhat.thermostat.common.dao.NetworkInterfaceInfoDAO;
@@ -63,9 +65,21 @@
 
     @Before
     public void setUp() {
+        Storage s = mock(Storage.class);
+        CpuStatDAO cDAO = mock(CpuStatDAO.class);
+        HostInfoDAO hDAO = mock(HostInfoDAO.class);
+        MemoryStatDAO mDAO = mock(MemoryStatDAO.class);
+        VmCpuStatDAO vDAO = mock(VmCpuStatDAO.class);
+        NetworkInterfaceInfoDAO nDAO = mock(NetworkInterfaceInfoDAO.class);
+        DAOFactory df = mock(DAOFactory.class);
+        when(df.getStorage()).thenReturn(s);
+        when(df.getCpuStatDAO()).thenReturn(cDAO);
+        when(df.getHostInfoDAO()).thenReturn(hDAO);
+        when(df.getMemoryStatDAO()).thenReturn(mDAO);
+        when(df.getVmCpuStatDAO()).thenReturn(vDAO);
+        when(df.getNetworkInterfaceInfoDAO()).thenReturn(nDAO);
         b = new SystemBackend();
-        Storage s = mock(Storage.class);
-        b.setStorage(s);
+        b.setDAOFactory(df);
     }
 
     @Test
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/main/java/com/redhat/thermostat/client/AgentConfigurationSource.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class AgentConfigurationSource {
+
+    // FIXME fix this properly
+
+    public List<String> getKnownAgents() {
+        return Arrays.asList(new String[] { "Agent Smith", "Agent Jones", "Agent Brown" });
+    }
+
+    public Map<String, Boolean> getAgentBackends(String agentName) {
+        Map<String, Boolean> fake = new HashMap<>();
+        fake.put("Monitor New JVMs", true);
+        fake.put("Use up all my CPU Cycles", false);
+        return fake;
+    }
+
+    public void updateAgentConfig(String agentName, Map<String, Boolean> newBackendStatus) {
+        // TODO Auto-generated method stub
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/main/java/com/redhat/thermostat/client/DefaultViewFactory.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.common.View;
+import com.redhat.thermostat.common.ViewFactory;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+
+public class DefaultViewFactory implements ViewFactory {
+
+    private static final Logger logger = LoggingUtils.getLogger(SwingViewFactory.class);
+    private final Map<Class<?>, Class<?>> lookupTable = Collections.synchronizedMap(new HashMap<Class<?>, Class<?>>());
+
+    @Override
+    public <T extends View> T getView(Class<T> viewClass) {
+        Class<? extends T> klass = getViewClass(viewClass);
+        if (klass == null) {
+            logger.log(Level.WARNING, "no view class registered for " + viewClass.toString());
+            return null;
+        }
+
+        try {
+            return klass.newInstance();
+        } catch (InstantiationException | IllegalAccessException e) {
+            logger.log(Level.WARNING, "error instantiaitng" + klass);
+            return null;
+        }
+    }
+
+    @Override
+    public <T extends View> Class<? extends T> getViewClass(Class<T> viewClass) {
+        // the cast is safe because the only way to insert an entry into the table is through
+        // a method that enforces this constraint
+        @SuppressWarnings("unchecked")
+        Class<? extends T> result = (Class<? extends T>) lookupTable.get(viewClass);
+        return result;
+    }
+
+    @Override
+    public <T extends View> void setViewClass(Class<T> viewClass, Class<? extends T> implClass) {
+        lookupTable.put(viewClass, implClass);
+    }
+
+}
\ No newline at end of file
--- a/client/src/main/java/com/redhat/thermostat/client/Main.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/Main.java	Mon Apr 16 15:41:08 2012 +0200
@@ -47,6 +47,7 @@
 import javax.swing.JPopupMenu;
 import javax.swing.SwingUtilities;
 
+import com.redhat.thermostat.client.config.ClientPreferences;
 import com.redhat.thermostat.client.config.ConnectionConfiguration;
 import com.redhat.thermostat.client.locale.LocaleResources;
 import com.redhat.thermostat.client.ui.ConnectionSelectionDialog;
@@ -56,13 +57,13 @@
 import com.redhat.thermostat.common.TimerFactory;
 import com.redhat.thermostat.common.appctx.ApplicationContext;
 import com.redhat.thermostat.common.config.StartupConfiguration;
-import com.redhat.thermostat.common.dao.Connection;
-import com.redhat.thermostat.common.dao.Connection.ConnectionListener;
-import com.redhat.thermostat.common.dao.Connection.ConnectionStatus;
-import com.redhat.thermostat.common.dao.ConnectionProvider;
 import com.redhat.thermostat.common.dao.DAOFactory;
-import com.redhat.thermostat.common.dao.MongoConnectionProvider;
 import com.redhat.thermostat.common.dao.MongoDAOFactory;
+import com.redhat.thermostat.common.storage.Connection;
+import com.redhat.thermostat.common.storage.StorageProvider;
+import com.redhat.thermostat.common.storage.MongoStorageProvider;
+import com.redhat.thermostat.common.storage.Connection.ConnectionListener;
+import com.redhat.thermostat.common.storage.Connection.ConnectionStatus;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 
 public class Main {
@@ -80,13 +81,16 @@
             System.exit(-1);
         }
 
-        StartupConfiguration config = new ConnectionConfiguration();
-        
-        ConnectionProvider connProv = new MongoConnectionProvider(config);
+        ClientPreferences prefs = new ClientPreferences();
+        StartupConfiguration config = new ConnectionConfiguration(prefs);
+
+        StorageProvider connProv = new MongoStorageProvider(config);
         DAOFactory daoFactory = new MongoDAOFactory(connProv);
         ApplicationContext.getInstance().setDAOFactory(daoFactory);
         TimerFactory timerFactory = new ThreadPoolTimerFactory(1);
         ApplicationContext.getInstance().setTimerFactory(timerFactory);
+        SwingViewFactory viewFactory = new SwingViewFactory();
+        ApplicationContext.getInstance().setViewFactory(viewFactory);
     }
 
     private void showGui() {
--- a/client/src/main/java/com/redhat/thermostat/client/MainView.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/MainView.java	Mon Apr 16 15:41:08 2012 +0200
@@ -43,7 +43,10 @@
 
     enum Action {
         HOST_VM_TREE_FILTER,
-        SHUTDOWN
+        SHOW_AGENT_CONFIG,
+        SHOW_CLIENT_CONFIG,
+        SWITCH_HISTORY_MODE,
+        SHUTDOWN,
     }
 
     void addActionListener(ActionListener<Action> capture);
--- a/client/src/main/java/com/redhat/thermostat/client/MainWindowControllerImpl.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/MainWindowControllerImpl.java	Mon Apr 16 15:41:08 2012 +0200
@@ -38,8 +38,13 @@
 
 import java.util.Collection;
 import java.util.concurrent.TimeUnit;
-import java.util.logging.Logger;
 
+import com.redhat.thermostat.client.config.ClientPreferences;
+import com.redhat.thermostat.client.ui.AgentConfigurationController;
+import com.redhat.thermostat.client.ui.AgentConfigurationModel;
+import com.redhat.thermostat.client.ui.AgentConfigurationView;
+import com.redhat.thermostat.client.ui.ClientConfigurationController;
+import com.redhat.thermostat.client.ui.ClientConfigurationView;
 import com.redhat.thermostat.common.ActionEvent;
 import com.redhat.thermostat.common.ActionListener;
 import com.redhat.thermostat.common.Timer;
@@ -49,7 +54,6 @@
 import com.redhat.thermostat.common.dao.HostRef;
 import com.redhat.thermostat.common.dao.VmInfoDAO;
 import com.redhat.thermostat.common.dao.VmRef;
-import com.redhat.thermostat.common.utils.LoggingUtils;
 
 public class MainWindowControllerImpl implements MainWindowController {
 
@@ -62,6 +66,8 @@
     private final HostInfoDAO hostsDAO;
     private final VmInfoDAO vmsDAO;
 
+    private boolean showHistory;
+    
     public MainWindowControllerImpl(MainView view) {
 
         ApplicationContext ctx = ApplicationContext.getInstance();
@@ -78,14 +84,18 @@
 
         @Override
         public Collection<HostRef> getHosts() {
-            return hostsDAO.getHosts();
+            if (showHistory) {
+                return hostsDAO.getHosts();
+            } else {
+                return hostsDAO.getAliveHosts();
+            }
         }
 
         @Override
         public Collection<VmRef> getVMs(HostRef host) {
             return vmsDAO.getVMs(host);
         }
-        
+
     }
 
     private void initializeTimer() {
@@ -135,6 +145,15 @@
                     String filter = view.getHostVmTreeFilter();
                     setHostVmTreeFilter(filter);
                     break;
+                case SHOW_AGENT_CONFIG:
+                    showAgentConfiguration();
+                    break;
+                case SHOW_CLIENT_CONFIG:
+                    showConfigureClientPreferences();
+                    break;
+                case SWITCH_HISTORY_MODE:
+                    switchHistoryMode();
+                    break;
                 case SHUTDOWN:
                     stop();
                     break;
@@ -142,7 +161,6 @@
                     assert false;
                 }
             }
-            
         });
     }
 
@@ -151,4 +169,23 @@
         view.showMainWindow();
     }
 
+    private void showAgentConfiguration() {
+        AgentConfigurationSource agentPrefs = new AgentConfigurationSource();
+        AgentConfigurationModel model = new AgentConfigurationModel(agentPrefs);
+        AgentConfigurationView view = ApplicationContext.getInstance().getViewFactory().getView(AgentConfigurationView.class);
+        AgentConfigurationController controller = new AgentConfigurationController(model, view);
+        controller.showView();
+    }
+
+    private void showConfigureClientPreferences() {
+        ClientPreferences prefs = new ClientPreferences();
+        ClientConfigurationView view = ApplicationContext.getInstance().getViewFactory().getView(ClientConfigurationView.class);
+        ClientConfigurationController controller = new ClientConfigurationController(prefs, view);
+        controller.showDialog();
+    }
+    
+    private void switchHistoryMode() {
+        showHistory = !showHistory;
+        doUpdateTreeAsync();
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/main/java/com/redhat/thermostat/client/SwingViewFactory.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client;
+
+
+import com.redhat.thermostat.client.ui.AgentConfigurationFrame;
+import com.redhat.thermostat.client.ui.AgentConfigurationView;
+import com.redhat.thermostat.client.ui.ClientConfigurationFrame;
+import com.redhat.thermostat.client.ui.ClientConfigurationView;
+import com.redhat.thermostat.client.ui.HostCpuPanel;
+import com.redhat.thermostat.client.ui.HostCpuView;
+import com.redhat.thermostat.client.ui.HostMemoryPanel;
+import com.redhat.thermostat.client.ui.HostMemoryView;
+import com.redhat.thermostat.client.ui.HostOverviewPanel;
+import com.redhat.thermostat.client.ui.HostOverviewView;
+import com.redhat.thermostat.client.ui.VmClassStatPanel;
+import com.redhat.thermostat.client.ui.VmClassStatView;
+import com.redhat.thermostat.client.ui.VmCpuPanel;
+import com.redhat.thermostat.client.ui.VmCpuView;
+import com.redhat.thermostat.client.ui.VmGcPanel;
+import com.redhat.thermostat.client.ui.VmGcView;
+import com.redhat.thermostat.client.ui.VmMemoryPanel;
+import com.redhat.thermostat.client.ui.VmMemoryView;
+import com.redhat.thermostat.client.ui.VmOverviewPanel;
+import com.redhat.thermostat.client.ui.VmOverviewView;
+import com.redhat.thermostat.common.ViewFactory;
+
+public class SwingViewFactory extends DefaultViewFactory implements ViewFactory {
+
+    public SwingViewFactory() {
+        setViewClass(AgentConfigurationView.class, AgentConfigurationFrame.class);
+        setViewClass(ClientConfigurationView.class, ClientConfigurationFrame.class);
+
+        setViewClass(HostCpuView.class, HostCpuPanel.class);
+        setViewClass(HostMemoryView.class, HostMemoryPanel.class);
+        setViewClass(HostOverviewView.class, HostOverviewPanel.class);
+
+        setViewClass(VmClassStatView.class, VmClassStatPanel.class);
+        setViewClass(VmCpuView.class, VmCpuPanel.class);
+        setViewClass(VmGcView.class, VmGcPanel.class);
+        setViewClass(VmMemoryView.class, VmMemoryPanel.class);
+        setViewClass(VmOverviewView.class, VmOverviewPanel.class);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/main/java/com/redhat/thermostat/client/config/ClientPreferences.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.config;
+
+import java.util.prefs.BackingStoreException;
+import java.util.prefs.Preferences;
+
+public class ClientPreferences {
+
+    private static final String CONNECTION_URL = "connection-url";
+
+    private final Preferences prefs;
+
+    public ClientPreferences() {
+        this(Preferences.userRoot().node("thermostat"));
+    }
+
+    ClientPreferences(Preferences prefs) {
+        this.prefs = prefs;
+    }
+
+    public String getConnectionUrl() {
+        return prefs.get(CONNECTION_URL, "mongodb://127.0.0.1:27518");
+    }
+
+    public void setConnectionUrl(String url) {
+        prefs.put(CONNECTION_URL, url);
+    }
+
+    public void flush() throws BackingStoreException {
+        prefs.flush();
+    }
+}
--- a/client/src/main/java/com/redhat/thermostat/client/config/ConnectionConfiguration.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/config/ConnectionConfiguration.java	Mon Apr 16 15:41:08 2012 +0200
@@ -40,10 +40,14 @@
 
 public class ConnectionConfiguration implements StartupConfiguration {
 
+    private final ClientPreferences clientPrefs;
+
+    public ConnectionConfiguration(ClientPreferences clientPrefs) {
+        this.clientPrefs = clientPrefs;
+    }
+
     @Override
     public String getDBConnectionString() {
-        // TODO: this needs to be read from preferences or from the agent
-        // configuration
-        return "mongodb://127.0.0.1:27518";
+        return clientPrefs.getConnectionUrl();
     }
 }
--- a/client/src/main/java/com/redhat/thermostat/client/locale/LocaleResources.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/locale/LocaleResources.java	Mon Apr 16 15:41:08 2012 +0200
@@ -55,9 +55,13 @@
     MENU_FILE_IMPORT,
     MENU_FILE_EXPORT,
     MENU_FILE_EXIT,
+    MENU_EDIT,
+    MENU_EDIT_CONFIGURE_AGENT,
+    MENU_EDIT_CONFIGURE_CLIENT,
+    MENU_EDIT_ENABLE_HISTORY_MODE,
     MENU_HELP,
     MENU_HELP_ABOUT,
-
+    
     GARBAGE_COLLECTION,
     YOUNG_GEN,
     EDEN_GEN,
@@ -172,7 +176,16 @@
 
     VM_LOADED_CLASSES,
     VM_CLASSES_CHART_REAL_TIME_LABEL,
-    VM_CLASSES_CHART_LOADED_CLASSES_LABEL;
+    VM_CLASSES_CHART_LOADED_CLASSES_LABEL,
+
+    CONFIGURE_AGENT_WINDOW_TITLE,
+    CONFIGURE_AGENT_AGENTS_LIST,
+    CONFIGURE_ENABLE_BACKENDS,
+
+    CLIENT_PREFS_WINDOW_TITLE,
+    CLIENT_PREFS_GENERAL,
+    CLIENT_PREFS_STORAGE_URL,
+    ;
 
     static final String RESOURCE_BUNDLE =
             "com.redhat.thermostat.client.locale.strings";
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/AgentConfigurationController.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.ui;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import com.redhat.thermostat.client.ui.AgentConfigurationView.ConfigurationAction;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+
+public class AgentConfigurationController implements ActionListener<ConfigurationAction> {
+
+    private final AgentConfigurationView view;
+    private final AgentConfigurationModel model;
+    private String agentName = null;
+
+    public AgentConfigurationController(AgentConfigurationModel model, AgentConfigurationView view) {
+        this.view = view;
+        this.model = model;
+
+        view.addActionListener(this);
+
+    }
+
+    public void showView() {
+        Collection<String> agents = model.getAgents();
+        agentName = null;
+        for (String agentName: agents) {
+            if (this.agentName == null) {
+                this.agentName = agentName;
+            }
+            view.addAgent(agentName);
+        }
+        view.showDialog();
+        updateViewFromModel();
+    }
+
+    public void hideView() {
+        view.hideDialog();
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent<ConfigurationAction> actionEvent) {
+        switch (actionEvent.getActionId()) {
+            case SWITCH_AGENT:
+                updateModelFromCurrentView();
+                String agentName = view.getSelectedAgent();
+                this.agentName = agentName;
+                updateViewFromModel();
+                break;
+            case CLOSE_ACCEPT:
+                updateModelFromCurrentView();
+                model.saveConfiguration();
+                /* fall through */
+            case CLOSE_CANCEL:
+                hideView();
+                break;
+            default:
+                throw new IllegalArgumentException("unknown event");
+        }
+    }
+
+    private void updateModelFromCurrentView() {
+        Map<String, Boolean> map = view.getBackendStatus();
+        for (Entry<String, Boolean> entry: map.entrySet()) {
+            model.setBackendEnabled(agentName, entry.getKey(), entry.getValue());
+        }
+
+    }
+
+    private void updateViewFromModel() {
+        Map<String, Boolean> map = new HashMap<>();
+        for (String backendName: model.getBackends(agentName)) {
+            map.put(backendName, model.getAgentBackendEnabled(agentName, backendName));
+        }
+        view.setBackendStatus(map);
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/AgentConfigurationFrame.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,308 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.ui;
+
+import static com.redhat.thermostat.client.locale.Translate.localize;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.swing.Box;
+import javax.swing.DefaultListModel;
+import javax.swing.GroupLayout;
+import javax.swing.GroupLayout.Alignment;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.LayoutStyle.ComponentPlacement;
+import javax.swing.SwingUtilities;
+
+import com.redhat.thermostat.client.locale.LocaleResources;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import javax.swing.JScrollPane;
+import javax.swing.JList;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+public class AgentConfigurationFrame extends JFrame implements AgentConfigurationView, java.awt.event.ActionListener, ListSelectionListener {
+
+    private static final long serialVersionUID = -6049272471909474886L;
+
+    private final CopyOnWriteArrayList<ActionListener<ConfigurationAction>> listeners = new CopyOnWriteArrayList<>();
+
+    private final Map<String, JCheckBox> backends = Collections.synchronizedMap(new HashMap<String, JCheckBox>());
+
+    private final JPanel availableBackendsPanel;
+    private final GridBagConstraints availableBackendsPanelContstraints = new GridBagConstraints();
+
+    private final JButton okayButton;
+    private final JButton cancelButton;
+
+    private final JList<String> agentList;
+    private final DefaultListModel<String> listModel;
+
+
+    public AgentConfigurationFrame() {
+        assertInEDT();
+
+        setTitle(localize(LocaleResources.CONFIGURE_AGENT_WINDOW_TITLE));
+
+        JLabel lblEnabledisableBackends = new JLabel(localize(LocaleResources.CONFIGURE_ENABLE_BACKENDS));
+
+        availableBackendsPanel = new JPanel();
+
+        okayButton = new JButton(localize(LocaleResources.BUTTON_OK));
+        okayButton.addActionListener(this);
+
+        cancelButton = new JButton(localize(LocaleResources.BUTTON_CANCEL));
+        cancelButton.addActionListener(this);
+
+        JScrollPane scrollPane = new JScrollPane();
+
+        JLabel lblAgents = new JLabel(localize(LocaleResources.CONFIGURE_AGENT_AGENTS_LIST));
+
+        GroupLayout groupLayout = new GroupLayout(getContentPane());
+        groupLayout.setHorizontalGroup(
+            groupLayout.createParallelGroup(Alignment.LEADING)
+                .addGroup(groupLayout.createSequentialGroup()
+                    .addContainerGap()
+                    .addGroup(groupLayout.createParallelGroup(Alignment.LEADING)
+                        .addGroup(groupLayout.createSequentialGroup()
+                            .addComponent(scrollPane, GroupLayout.PREFERRED_SIZE, 127, GroupLayout.PREFERRED_SIZE)
+                            .addGap(0)
+                            .addGroup(groupLayout.createParallelGroup(Alignment.LEADING)
+                                .addGroup(Alignment.TRAILING, groupLayout.createSequentialGroup()
+                                    .addComponent(cancelButton)
+                                    .addPreferredGap(ComponentPlacement.RELATED)
+                                    .addComponent(okayButton))
+                                .addGroup(groupLayout.createSequentialGroup()
+                                    .addGap(12)
+                                    .addGroup(groupLayout.createParallelGroup(Alignment.LEADING)
+                                        .addGroup(groupLayout.createSequentialGroup()
+                                            .addGap(12)
+                                            .addComponent(availableBackendsPanel, GroupLayout.DEFAULT_SIZE, 540, Short.MAX_VALUE))
+                                        .addComponent(lblEnabledisableBackends)))))
+                        .addComponent(lblAgents))
+                    .addContainerGap())
+        );
+        groupLayout.setVerticalGroup(
+            groupLayout.createParallelGroup(Alignment.TRAILING)
+                .addGroup(groupLayout.createSequentialGroup()
+                    .addGap(6)
+                    .addComponent(lblAgents)
+                    .addPreferredGap(ComponentPlacement.UNRELATED)
+                    .addGroup(groupLayout.createParallelGroup(Alignment.TRAILING)
+                        .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 413, Short.MAX_VALUE)
+                        .addGroup(groupLayout.createSequentialGroup()
+                            .addComponent(lblEnabledisableBackends)
+                            .addGap(2)
+                            .addComponent(availableBackendsPanel, GroupLayout.DEFAULT_SIZE, 365, Short.MAX_VALUE)
+                            .addPreferredGap(ComponentPlacement.RELATED)
+                            .addGroup(groupLayout.createParallelGroup(Alignment.BASELINE)
+                                .addComponent(okayButton)
+                                .addComponent(cancelButton))))
+                    .addContainerGap())
+        );
+
+        listModel = new DefaultListModel<String>();
+        agentList = new JList<String>(listModel);
+        agentList.addListSelectionListener(this);
+        scrollPane.setViewportView(agentList);
+
+        availableBackendsPanel.setLayout(new GridBagLayout());
+        getContentPane().setLayout(groupLayout);
+
+        addWindowListener(new WindowAdapter() {
+            @Override
+            public void windowClosing(WindowEvent e) {
+                fireAction(new ActionEvent<>(AgentConfigurationFrame.this, ConfigurationAction.CLOSE_CANCEL));
+            }
+        });
+    }
+
+    private void resetConstraints() {
+        availableBackendsPanelContstraints.gridwidth = 1;
+        availableBackendsPanelContstraints.gridy = 0;
+        availableBackendsPanelContstraints.gridx = 0;
+        availableBackendsPanelContstraints.weightx = 0;
+        availableBackendsPanelContstraints.weighty = 0;
+        availableBackendsPanelContstraints.anchor = GridBagConstraints.LINE_START;
+        availableBackendsPanelContstraints.fill = GridBagConstraints.BOTH;
+    }
+
+
+    @Override
+    public void addActionListener(ActionListener<ConfigurationAction> listener) {
+        listeners.add(listener);
+    }
+
+    @Override
+    public void removeActionListener(ActionListener<ConfigurationAction> listener) {
+        listeners.remove(listener);
+    }
+
+    @Override
+    public void addAgent(final String agentName) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                listModel.addElement(agentName);
+            }
+        });
+    }
+
+    @Override
+    public String getSelectedAgent() {
+        assertInEDT();
+        return agentList.getSelectedValue();
+    }
+
+    @Override
+    public void clearAllAgents() {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                listModel.clear();
+            }
+        });
+    }
+
+
+    @Override
+    public void setBackendStatus(final Map<String, Boolean> backendStatus) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                backends.clear();
+                availableBackendsPanel.removeAll();
+                resetConstraints();
+
+                for (Entry<String, Boolean> entry: backendStatus.entrySet()) {
+                    String backendName = entry.getKey();
+                    boolean checked = entry.getValue();
+                    JCheckBox checkBox = new JCheckBox(backendName);
+                    checkBox.setSelected(checked);
+                    checkBox.setActionCommand(backendName);
+                    checkBox.addActionListener(AgentConfigurationFrame.this);
+                    backends.put(backendName, checkBox);
+                    availableBackendsPanel.add(checkBox, availableBackendsPanelContstraints);
+                    availableBackendsPanelContstraints.gridy++;
+                }
+                availableBackendsPanelContstraints.weighty = 1.0;
+                availableBackendsPanelContstraints.weightx = 1.0;
+                availableBackendsPanelContstraints.fill = GridBagConstraints.BOTH;
+                availableBackendsPanel.add(Box.createGlue(), availableBackendsPanelContstraints);
+                AgentConfigurationFrame.this.revalidate();
+            }
+        });
+    }
+
+    @Override
+    public Map<String, Boolean> getBackendStatus() {
+        assertInEDT();
+
+        Map<String,Boolean> latestUserSpecified = new HashMap<>();
+        for (Entry<String, JCheckBox> entry: backends.entrySet()) {
+            latestUserSpecified.put(entry.getKey(), entry.getValue().isSelected());
+        }
+        return latestUserSpecified;
+    }
+
+    @Override
+    public void showDialog() {
+        assertInEDT();
+
+        pack();
+        setVisible(true);
+
+        agentList.setSelectedIndex(0);
+    }
+
+    @Override
+    public void hideDialog() {
+        assertInEDT();
+
+        setVisible(false);
+        dispose();
+    }
+
+    @Override
+    public void actionPerformed(java.awt.event.ActionEvent e) {
+        Object source = e.getSource();
+        if (source == okayButton) {
+            fireAction(new ActionEvent<>(this, ConfigurationAction.CLOSE_ACCEPT));
+        } else if (source == cancelButton) {
+            fireAction(new ActionEvent<>(this, ConfigurationAction.CLOSE_CANCEL));
+        }
+    }
+
+    @Override
+    public void valueChanged(ListSelectionEvent e) {
+        if (e.getSource() == agentList) {
+            if (e.getValueIsAdjusting()) {
+                return;
+            }
+            fireAction(new ActionEvent<>(this, ConfigurationAction.SWITCH_AGENT));
+        } else {
+            throw new IllegalStateException("unknown trigger");
+        }
+    }
+
+    private void fireAction(ActionEvent<ConfigurationAction> actionEvent) {
+        for (ActionListener<ConfigurationAction> l: listeners) {
+            l.actionPerformed(actionEvent);
+        }
+    }
+
+    private static void assertInEDT() {
+        if (!SwingUtilities.isEventDispatchThread()) {
+            throw new IllegalStateException("must be called from within the swing EDT");
+        }
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/AgentConfigurationModel.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.ui;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import com.redhat.thermostat.client.AgentConfigurationSource;
+
+/**
+ * This model sits between the current view and the remote model, and allows
+ * us to make changes and later throw them away.
+ */
+public class AgentConfigurationModel {
+
+    private final AgentConfigurationSource remoteConfiguration;
+
+    private final List<String> knownAgents;
+    private Map<String, Map<String, Boolean>> enabledBackends;
+
+    public AgentConfigurationModel(AgentConfigurationSource configSource) {
+        this.remoteConfiguration = configSource;
+
+        knownAgents = new ArrayList<>(remoteConfiguration.getKnownAgents());
+        enabledBackends = new HashMap<>();
+        for (String agent: knownAgents) {
+            enabledBackends.put(agent, new HashMap<String, Boolean>(remoteConfiguration.getAgentBackends(agent)));
+        }
+    }
+
+    public Collection<String> getAgents() {
+        return Collections.unmodifiableList(knownAgents);
+    }
+
+    public Collection<String> getBackends(String agentName) {
+        return Collections.unmodifiableSet(enabledBackends.get(agentName).keySet());
+    }
+
+    public void setBackendEnabled(String agentName, String backendName, boolean enabled) {
+        enabledBackends.get(agentName).put(backendName, enabled);
+    }
+
+    public boolean getAgentBackendEnabled(String agentName, String backendName) {
+        return enabledBackends.get(agentName).get(backendName);
+    }
+
+    public void saveConfiguration() {
+        for (Entry<String, Map<String, Boolean>> entry: enabledBackends.entrySet()) {
+            remoteConfiguration.updateAgentConfig(entry.getKey(), entry.getValue());
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/AgentConfigurationView.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.ui;
+
+import java.util.Map;
+
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.View;
+
+public interface AgentConfigurationView extends View {
+
+    enum ConfigurationAction {
+        SWITCH_AGENT,
+        CLOSE_ACCEPT,
+        CLOSE_CANCEL,
+    }
+
+    void showDialog();
+
+    void hideDialog();
+
+    void addActionListener(ActionListener<ConfigurationAction> listener);
+
+    void removeActionListener(ActionListener<ConfigurationAction> listener);
+
+    void addAgent(String agentName);
+
+    String getSelectedAgent();
+
+    void clearAllAgents();
+
+    void setBackendStatus(Map<String,Boolean> agentConfiguration);
+
+    Map<String,Boolean> getBackendStatus();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/ClientConfigurationController.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.ui;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.prefs.BackingStoreException;
+
+import com.redhat.thermostat.client.config.ClientPreferences;
+import com.redhat.thermostat.client.ui.ClientConfigurationView.Action;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+
+public class ClientConfigurationController implements ActionListener<Action> {
+
+    private static final Logger logger = LoggingUtils.getLogger(ClientConfigurationController.class);
+
+    private final ClientConfigurationView view;
+    private final ClientPreferences model;
+
+    public ClientConfigurationController(ClientPreferences model, ClientConfigurationView view) {
+        this.model = model;
+        this.view = view;
+
+        view.addListener(this);
+    }
+
+    public void showDialog() {
+        updateViewFromModel();
+        view.showDialog();
+    }
+
+    private void updateViewFromModel() {
+        view.setConnectionUrl(model.getConnectionUrl());
+    }
+
+    private void updateModelFromView() {
+        model.setConnectionUrl(view.getConnectionUrl());
+
+        try {
+            model.flush();
+        } catch (BackingStoreException e) {
+            logger.log(Level.WARNING, "error saving client preferences", e);
+        }
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent<Action> actionEvent) {
+        if (actionEvent.getSource() != view) {
+            return;
+        }
+
+        switch (actionEvent.getActionId()) {
+            case CLOSE_ACCEPT:
+                updateModelFromView();
+                /* fall through */
+            case CLOSE_CANCEL:
+                view.hideDialog();
+                break;
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/ClientConfigurationFrame.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.ui;
+
+import static com.redhat.thermostat.client.locale.Translate.localize;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.swing.GroupLayout;
+import javax.swing.GroupLayout.Alignment;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
+import javax.swing.LayoutStyle.ComponentPlacement;
+
+import com.redhat.thermostat.client.locale.LocaleResources;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+
+public class ClientConfigurationFrame extends JFrame implements ClientConfigurationView, java.awt.event.ActionListener {
+
+    private static final long serialVersionUID = 6888957994092403516L;
+
+    private final JTextField storageUrl;
+    private final JButton btnOk;
+    private final JButton btnCancel;
+
+    private CopyOnWriteArrayList<ActionListener<Action>> listeners = new CopyOnWriteArrayList<>();
+
+    public ClientConfigurationFrame() {
+        setTitle(localize(LocaleResources.CLIENT_PREFS_WINDOW_TITLE));
+
+        btnOk = new JButton(localize(LocaleResources.BUTTON_OK));
+        btnOk.addActionListener(this);
+        btnOk.setName("ok");
+        btnCancel = new JButton(localize(LocaleResources.BUTTON_CANCEL));
+        btnCancel.addActionListener(this);
+        btnCancel.setName("cancel");
+        JLabel lblClientConfiguration = new JLabel(localize(LocaleResources.CLIENT_PREFS_GENERAL));
+
+        JLabel lblStorageUrl = new JLabel(localize(LocaleResources.CLIENT_PREFS_STORAGE_URL));
+
+        storageUrl = new JTextField();
+        storageUrl.setColumns(10);
+        storageUrl.setName("connectionUrl");
+
+        GroupLayout groupLayout = new GroupLayout(getContentPane());
+        groupLayout.setHorizontalGroup(
+            groupLayout.createParallelGroup(Alignment.LEADING)
+                .addGroup(Alignment.TRAILING, groupLayout.createSequentialGroup()
+                    .addContainerGap(251, Short.MAX_VALUE)
+                    .addComponent(btnCancel)
+                    .addPreferredGap(ComponentPlacement.RELATED)
+                    .addComponent(btnOk)
+                    .addContainerGap())
+                .addGroup(groupLayout.createSequentialGroup()
+                    .addContainerGap()
+                    .addGroup(groupLayout.createParallelGroup(Alignment.LEADING)
+                        .addGroup(groupLayout.createSequentialGroup()
+                            .addGap(12)
+                            .addComponent(lblStorageUrl)
+                            .addPreferredGap(ComponentPlacement.UNRELATED)
+                            .addComponent(storageUrl, GroupLayout.DEFAULT_SIZE, 305, Short.MAX_VALUE))
+                        .addComponent(lblClientConfiguration))
+                    .addContainerGap())
+        );
+        groupLayout.setVerticalGroup(
+            groupLayout.createParallelGroup(Alignment.LEADING)
+                .addGroup(Alignment.TRAILING, groupLayout.createSequentialGroup()
+                    .addContainerGap()
+                    .addComponent(lblClientConfiguration)
+                    .addPreferredGap(ComponentPlacement.RELATED)
+                    .addGroup(groupLayout.createParallelGroup(Alignment.BASELINE)
+                        .addComponent(lblStorageUrl)
+                        .addComponent(storageUrl, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                    .addPreferredGap(ComponentPlacement.RELATED, 185, Short.MAX_VALUE)
+                    .addGroup(groupLayout.createParallelGroup(Alignment.BASELINE)
+                        .addComponent(btnOk)
+                        .addComponent(btnCancel))
+                    .addContainerGap())
+        );
+        getContentPane().setLayout(groupLayout);
+    }
+
+    @Override
+    public void showDialog() {
+        assertInEDT();
+        this.pack();
+        this.setVisible(true);
+    }
+
+    @Override
+    public void hideDialog() {
+        assertInEDT();
+
+        this.setVisible(false);
+        this.dispose();
+    }
+
+    @Override
+    public String getConnectionUrl() {
+        assertInEDT();
+        return storageUrl.getText();
+    }
+
+    @Override
+    public void setConnectionUrl(final String url) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                storageUrl.setText(url);
+            }
+        });
+    }
+
+    @Override
+    public void actionPerformed(java.awt.event.ActionEvent e) {
+        if (e.getSource() == btnOk) {
+            fireAction(new ActionEvent<>(this, Action.CLOSE_ACCEPT));
+        } else if (e.getSource() == btnCancel) {
+            fireAction(new ActionEvent<>(this, Action.CLOSE_CANCEL));
+        }
+    }
+
+    @Override
+    public void addListener(ActionListener<Action> listener) {
+        listeners.add(listener);
+    }
+
+    @Override
+    public void removeListener(ActionListener<Action> listener) {
+        listeners.remove(listener);
+    }
+
+    private void fireAction(ActionEvent<Action> actionEvent) {
+        for (ActionListener<Action> listener: listeners) {
+            listener.actionPerformed(actionEvent);
+        }
+    }
+
+    private void assertInEDT() {
+        if (!SwingUtilities.isEventDispatchThread()) {
+            throw new IllegalStateException("must be invoked in the EDT");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/ClientConfigurationView.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.ui;
+
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.View;
+
+public interface ClientConfigurationView extends View {
+
+    enum Action {
+        CLOSE_CANCEL,
+        CLOSE_ACCEPT,
+    }
+
+    void addListener(ActionListener<Action> listener);
+
+    void removeListener(ActionListener<Action> listener);
+
+    void setConnectionUrl(String url);
+    String getConnectionUrl();
+
+    void showDialog();
+    void hideDialog();
+
+}
--- a/client/src/main/java/com/redhat/thermostat/client/ui/ConnectionSelectionDialog.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/ConnectionSelectionDialog.java	Mon Apr 16 15:41:08 2012 +0200
@@ -58,8 +58,8 @@
 import javax.swing.JPanel;
 
 import com.redhat.thermostat.client.locale.LocaleResources;
-import com.redhat.thermostat.common.dao.Connection;
-import com.redhat.thermostat.common.dao.Connection.ConnectionType;
+import com.redhat.thermostat.common.storage.Connection;
+import com.redhat.thermostat.common.storage.Connection.ConnectionType;
 
 public class ConnectionSelectionDialog extends JDialog {
 
--- a/client/src/main/java/com/redhat/thermostat/client/ui/HostCpuController.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/HostCpuController.java	Mon Apr 16 15:41:08 2012 +0200
@@ -64,7 +64,7 @@
 
     public HostCpuController(HostRef ref) {
         this.ref = ref;
-        view = createView();
+        view = ApplicationContext.getInstance().getViewFactory().getView(HostCpuView.class);
         view.clearCpuLoadData();
         DAOFactory daos = ApplicationContext.getInstance().getDAOFactory();
         hostInfoDAO = daos.getHostInfoDAO();
@@ -72,7 +72,7 @@
 
         backgroundUpdateTimer = ApplicationContext.getInstance().getTimerFactory().createTimer();
         backgroundUpdateTimer.setAction(new Runnable() {
-            
+
             @Override
             public void run() {
                 updateView();
@@ -114,10 +114,6 @@
         view.addCpuLoadData(result);
     }
 
-    protected HostCpuView createView() {
-        return new HostCpuPanel();
-    }
-
     public Component getComponent() {
         return view.getUiComponent();
     }
--- a/client/src/main/java/com/redhat/thermostat/client/ui/HostCpuView.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/HostCpuView.java	Mon Apr 16 15:41:08 2012 +0200
@@ -39,9 +39,10 @@
 import java.awt.Component;
 import java.util.List;
 
+import com.redhat.thermostat.common.View;
 import com.redhat.thermostat.common.model.DiscreteTimeData;
 
-public interface HostCpuView {
+public interface HostCpuView extends View {
 
     void setCpuCount(String count);
 
--- a/client/src/main/java/com/redhat/thermostat/client/ui/HostMemoryController.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/HostMemoryController.java	Mon Apr 16 15:41:08 2012 +0200
@@ -74,7 +74,7 @@
         hostInfoDAO = daos.getHostInfoDAO();
         memoryStatDAO = daos.getMemoryStatDAO();
 
-        view = createView();
+        view = ApplicationContext.getInstance().getViewFactory().getView(HostMemoryView.class);
 
         view.addMemoryChart(MemoryType.MEMORY_TOTAL.name(), localize(LocaleResources.HOST_MEMORY_TOTAL));
         view.addMemoryChart(MemoryType.MEMORY_FREE.name(), localize(LocaleResources.HOST_MEMORY_FREE));
@@ -117,10 +117,6 @@
         return view.getUiComponent();
     }
 
-    protected HostMemoryView createView() {
-        return new HostMemoryPanel();
-    }
-
     private void doMemoryChartUpdate() {
         List<DiscreteTimeData<? extends Number>> memFree = new LinkedList<>();
         List<DiscreteTimeData<? extends Number>> memTotal = new LinkedList<>();
@@ -128,7 +124,7 @@
         List<DiscreteTimeData<? extends Number>> buf = new LinkedList<>();
         List<DiscreteTimeData<? extends Number>> swapTotal = new LinkedList<>();
         List<DiscreteTimeData<? extends Number>> swapFree = new LinkedList<>();
-        
+
         for (MemoryStat stat : memoryStatDAO.getLatestMemoryStats(ref)) {
             long timeStamp = stat.getTimeStamp();
             memFree.add(new DiscreteTimeData<Long>(timeStamp, stat.getFree()));
--- a/client/src/main/java/com/redhat/thermostat/client/ui/HostMemoryView.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/HostMemoryView.java	Mon Apr 16 15:41:08 2012 +0200
@@ -39,9 +39,10 @@
 import java.awt.Component;
 import java.util.List;
 
+import com.redhat.thermostat.common.View;
 import com.redhat.thermostat.common.model.DiscreteTimeData;
 
-public interface HostMemoryView {
+public interface HostMemoryView extends View {
 
     public interface GraphVisibilityChangeListener {
         public void show(String tag);
--- a/client/src/main/java/com/redhat/thermostat/client/ui/HostOverviewController.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/HostOverviewController.java	Mon Apr 16 15:41:08 2012 +0200
@@ -87,15 +87,11 @@
         networkTableColumnVector.add(localize(LocaleResources.NETWORK_IPV6_COLUMN));
 
         backgroundUpdateTimer = new Timer();
-        view = createView();
+        view = ApplicationContext.getInstance().getViewFactory().getView(HostOverviewView.class);
 
         view.setNetworkTableColumns(networkTableColumnVector.toArray());
     }
 
-    public HostOverviewView createView() {
-        return new HostOverviewPanel();
-    }
-
     private void doNetworkTableUpdateAsync() {
         new NetworkTableModelUpdater().execute();
     }
--- a/client/src/main/java/com/redhat/thermostat/client/ui/HostOverviewView.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/HostOverviewView.java	Mon Apr 16 15:41:08 2012 +0200
@@ -38,7 +38,9 @@
 
 import java.awt.Component;
 
-public interface HostOverviewView {
+import com.redhat.thermostat.common.View;
+
+public interface HostOverviewView extends View {
 
     void setHostName(String newHostName);
 
--- a/client/src/main/java/com/redhat/thermostat/client/ui/MainWindow.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/MainWindow.java	Mon Apr 16 15:41:08 2012 +0200
@@ -43,6 +43,7 @@
 import java.awt.Component;
 import java.awt.Dimension;
 import java.awt.Insets;
+import java.awt.event.ActionEvent;
 import java.awt.event.InputEvent;
 import java.awt.event.KeyEvent;
 import java.awt.event.WindowAdapter;
@@ -54,6 +55,7 @@
 import java.util.concurrent.ExecutionException;
 
 import javax.swing.BorderFactory;
+import javax.swing.JCheckBoxMenuItem;
 import javax.swing.JFrame;
 import javax.swing.JLabel;
 import javax.swing.JMenu;
@@ -223,17 +225,17 @@
 
     private final ShutdownClient shutdownAction;
 
-    private ApplicationInfo appInfo; 
+    private ApplicationInfo appInfo;
 
     private ActionNotifier<Action> actionNotifier = new ActionNotifier<>(this);
 
-    private final DefaultMutableTreeNode publishedRoot = 
+    private final DefaultMutableTreeNode publishedRoot =
             new DefaultMutableTreeNode(localize(LocaleResources.MAIN_WINDOW_TREE_ROOT_NAME));
     private final DefaultTreeModel publishedTreeModel = new DefaultTreeModel(publishedRoot);
 
     public MainWindow(UiFacadeFactory facadeFactory) {
         super();
-        
+
         appInfo = new ApplicationInfo();
         setTitle(appInfo.getName());
 
@@ -292,6 +294,40 @@
         fileExitMenu.addActionListener(shutdownAction);
         fileMenu.add(fileExitMenu);
 
+        JMenu editMenu = new JMenu(localize(LocaleResources.MENU_EDIT));
+        mainMenuBar.add(editMenu);
+
+        JMenuItem configureAgentMenuItem = new JMenuItem(localize(LocaleResources.MENU_EDIT_CONFIGURE_AGENT));
+        configureAgentMenuItem.addActionListener(new java.awt.event.ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                fireViewAction(Action.SHOW_AGENT_CONFIG);
+            }
+        });
+        editMenu.add(configureAgentMenuItem);
+
+        JMenuItem configureClientMenuItem = new JMenuItem(localize(LocaleResources.MENU_EDIT_CONFIGURE_CLIENT));
+        configureClientMenuItem.setName("showClientConfig");
+        configureClientMenuItem.addActionListener(new java.awt.event.ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                fireViewAction(Action.SHOW_CLIENT_CONFIG);
+            }
+        });
+        editMenu.add(configureClientMenuItem);
+        
+        editMenu.addSeparator();
+        JMenuItem historyModeMenuItem = new JCheckBoxMenuItem(localize(LocaleResources.MENU_EDIT_ENABLE_HISTORY_MODE));
+        historyModeMenuItem.setName("historyModeSwitch");
+        historyModeMenuItem.setSelected(false);
+        historyModeMenuItem.addActionListener(new java.awt.event.ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                fireViewAction(Action.SWITCH_HISTORY_MODE);
+            }
+        });
+        
+        editMenu.add(historyModeMenuItem);
         JMenu helpMenu = new JMenu(localize(LocaleResources.MENU_HELP));
         helpMenu.getPopupMenu().setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY, 1));
         mainMenuBar.add(helpMenu);
@@ -499,6 +535,7 @@
         }
     }
 
+    @Override
     public void addActionListener(ActionListener<Action> l) {
         actionNotifier.addActionListener(l);
     }
@@ -511,6 +548,7 @@
         actionNotifier.fireAction(action);
     }
 
+    @Override
     public void updateTree(String filter, HostsVMsLoader hostsVMsLoader) {
         BackgroundTreeModelWorker worker = new BackgroundTreeModelWorker(publishedTreeModel, publishedRoot, filter, hostsVMsLoader);
         worker.execute();
--- a/client/src/main/java/com/redhat/thermostat/client/ui/VmClassStatController.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/VmClassStatController.java	Mon Apr 16 15:41:08 2012 +0200
@@ -75,11 +75,7 @@
     public VmClassStatController(VmRef ref) {
         this.ref = ref;
         dao = ApplicationContext.getInstance().getDAOFactory().getVmClassStatsDAO();
-        classesView = createView();
-    }
-
-    protected VmClassStatView createView() {
-        return new VmClassStatPanel();
+        classesView = ApplicationContext.getInstance().getViewFactory().getView(VmClassStatView.class);
     }
 
     @Override
--- a/client/src/main/java/com/redhat/thermostat/client/ui/VmClassStatView.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/VmClassStatView.java	Mon Apr 16 15:41:08 2012 +0200
@@ -39,9 +39,10 @@
 import java.awt.Component;
 import java.util.List;
 
+import com.redhat.thermostat.common.View;
 import com.redhat.thermostat.common.model.DiscreteTimeData;
 
-public interface VmClassStatView {
+public interface VmClassStatView extends View {
 
     void clearClassCount();
 
--- a/client/src/main/java/com/redhat/thermostat/client/ui/VmCpuController.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/VmCpuController.java	Mon Apr 16 15:41:08 2012 +0200
@@ -61,12 +61,9 @@
     public VmCpuController(VmRef ref) {
         this.ref = ref;
         dao = ApplicationContext.getInstance().getDAOFactory().getVmCpuStatDAO();
-        view = createView();
+        view = ApplicationContext.getInstance().getViewFactory().getView(VmCpuView.class);
     }
 
-    protected VmCpuView createView() {
-        return new VmCpuPanel();
-    }
 
     @Override
     public void start() {
--- a/client/src/main/java/com/redhat/thermostat/client/ui/VmCpuView.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/VmCpuView.java	Mon Apr 16 15:41:08 2012 +0200
@@ -39,9 +39,10 @@
 import java.awt.Component;
 import java.util.List;
 
+import com.redhat.thermostat.common.View;
 import com.redhat.thermostat.common.model.DiscreteTimeData;
 
-public interface VmCpuView {
+public interface VmCpuView extends View {
 
     void addData(List<DiscreteTimeData<? extends Number>> data);
 
--- a/client/src/main/java/com/redhat/thermostat/client/ui/VmGcController.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/VmGcController.java	Mon Apr 16 15:41:08 2012 +0200
@@ -75,17 +75,13 @@
 
     public VmGcController(VmRef ref) {
         this.ref = ref;
-        this.view = createView();
+        this.view = ApplicationContext.getInstance().getViewFactory().getView(VmGcView.class);
 
         DAOFactory df = ApplicationContext.getInstance().getDAOFactory();
         gcDao = df.getVmGcStatDAO();
         memDao = df.getVmMemoryStatDAO();
     }
 
-    protected VmGcView createView() {
-        return new VmGcPanel();
-    }
-
     @Override
     public void start() {
         timer.scheduleAtFixedRate(new TimerTask() {
--- a/client/src/main/java/com/redhat/thermostat/client/ui/VmGcView.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/VmGcView.java	Mon Apr 16 15:41:08 2012 +0200
@@ -39,9 +39,10 @@
 import java.awt.Component;
 import java.util.List;
 
+import com.redhat.thermostat.common.View;
 import com.redhat.thermostat.common.model.DiscreteTimeData;
 
-public interface VmGcView {
+public interface VmGcView extends View {
 
     void addChart(String tag, String title);
 
--- a/client/src/main/java/com/redhat/thermostat/client/ui/VmMemoryController.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/VmMemoryController.java	Mon Apr 16 15:41:08 2012 +0200
@@ -61,7 +61,7 @@
     public VmMemoryController(VmRef ref) {
         this.ref = ref;
         dao = ApplicationContext.getInstance().getDAOFactory().getVmMemoryStatDAO();
-        view = createView();
+        view = ApplicationContext.getInstance().getViewFactory().getView(VmMemoryView.class);
     }
 
     @Override
@@ -88,10 +88,6 @@
         timer.cancel();
     }
 
-    protected VmMemoryView createView() {
-        return new VmMemoryPanel();
-    }
-
     public Component getComponent() {
         return view.getUiComponent();
     }
--- a/client/src/main/java/com/redhat/thermostat/client/ui/VmMemoryView.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/VmMemoryView.java	Mon Apr 16 15:41:08 2012 +0200
@@ -38,7 +38,9 @@
 
 import java.awt.Component;
 
-public interface VmMemoryView {
+import com.redhat.thermostat.common.View;
+
+public interface VmMemoryView extends View {
 
     void setMemoryRegionSize(String name, long used, long allocated, long max);
 
--- a/client/src/main/java/com/redhat/thermostat/client/ui/VmOverviewController.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/VmOverviewController.java	Mon Apr 16 15:41:08 2012 +0200
@@ -64,7 +64,7 @@
 
     public VmOverviewController(VmRef vmRef) {
         this.ref = vmRef;
-        this.view = createView();
+        this.view = ApplicationContext.getInstance().getViewFactory().getView(VmOverviewView.class);
 
         dao = ApplicationContext.getInstance().getDAOFactory().getVmInfoDAO();
 
@@ -110,10 +110,6 @@
         timer.cancel();
     }
 
-    protected VmOverviewView createView() {
-        return new VmOverviewPanel();
-    }
-
     public Component getComponent() {
         return view.getUiComponent();
     }
--- a/client/src/main/java/com/redhat/thermostat/client/ui/VmOverviewView.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/VmOverviewView.java	Mon Apr 16 15:41:08 2012 +0200
@@ -38,7 +38,9 @@
 
 import java.awt.Component;
 
-public interface VmOverviewView {
+import com.redhat.thermostat.common.View;
+
+public interface VmOverviewView extends View {
 
     void setVmPid(String pid);
 
--- a/client/src/main/resources/com/redhat/thermostat/client/locale/strings.properties	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/main/resources/com/redhat/thermostat/client/locale/strings.properties	Mon Apr 16 15:41:08 2012 +0200
@@ -15,6 +15,10 @@
 MENU_FILE_IMPORT = Import
 MENU_FILE_EXPORT = Export
 MENU_FILE_EXIT = Exit
+MENU_EDIT = Edit
+MENU_EDIT_CONFIGURE_AGENT = Configure Agent...
+MENU_EDIT_CONFIGURE_CLIENT = Client Preferences...
+MENU_EDIT_ENABLE_HISTORY_MODE = Enable History Mode
 MENU_HELP = Help
 MENU_HELP_ABOUT = About
 
@@ -135,3 +139,11 @@
 VM_CLASSES_CHART_REAL_TIME_LABEL = Time
 VM_CLASSES_CHART_LOADED_CLASSES_LABEL = Number of loaded classes
 VM_INFO_TAB_CLASSES = Classes
+
+CONFIGURE_AGENT_WINDOW_TITLE = Configure Agent Backends
+CONFIGURE_AGENT_AGENTS_LIST = Agents
+CONFIGURE_ENABLE_BACKENDS = Enable/Disable Backends
+
+CLIENT_PREFS_WINDOW_TITLE = Preferences
+CLIENT_PREFS_GENERAL = General
+CLIENT_PREFS_STORAGE_URL = Storage Url
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/test/java/com/redhat/thermostat/client/DefaultViewFactoryTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.common.View;
+
+public class DefaultViewFactoryTest {
+
+    public static class MockView implements View {}
+
+    @Test
+    public void testGetUnknownClasses() {
+        DefaultViewFactory factory = new DefaultViewFactory();
+        assertEquals(null, factory.getView(MockView.class));
+    }
+
+    @Test
+    public void testSetAndGet() {
+        DefaultViewFactory factory = new DefaultViewFactory();
+        factory.setViewClass(MockView.class, MockView.class);
+        assertEquals(MockView.class, factory.getViewClass(MockView.class));
+    }
+
+    @Test
+    public void testInstantiation() {
+        DefaultViewFactory factory = new DefaultViewFactory();
+        factory.setViewClass(MockView.class, MockView.class);
+        MockView view = factory.getView(MockView.class);
+        assertNotNull(view);
+        assertEquals(MockView.class.getName(), view.getClass().getName());
+    }
+}
--- a/client/src/test/java/com/redhat/thermostat/client/MainWindowControllerImplTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/test/java/com/redhat/thermostat/client/MainWindowControllerImplTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -79,6 +79,7 @@
     private HostInfoDAO mockHostsDAO;
     private VmInfoDAO mockVmsDAO;
 
+    @SuppressWarnings({ "unchecked", "rawtypes" }) // ActionListener fluff
     @Before
     public void setUp() {
         ApplicationContextUtil.resetApplicationContext();
@@ -149,7 +150,7 @@
         controller.showMainMainWindow();
         verify(view).showMainWindow();
     }
-
+    
     @Test
     public void verifyUpdateHostsVMsLoadsCorrectHosts() {
 
@@ -157,8 +158,8 @@
         expectedHosts.add(new HostRef("123", "fluffhost1"));
         expectedHosts.add(new HostRef("456", "fluffhost2"));
 
-        when(mockHostsDAO.getHosts()).thenReturn(expectedHosts);
-
+        when(mockHostsDAO.getAliveHosts()).thenReturn(expectedHosts);
+        
         controller.doUpdateTreeAsync();
 
         ArgumentCaptor<HostsVMsLoader> arg = ArgumentCaptor.forClass(HostsVMsLoader.class);
@@ -170,6 +171,35 @@
     }
 
     @Test
+    public void verifyHistoryModeUpdateHostsVMCorrectly() {
+
+        Collection<HostRef> liveHost = new ArrayList<>();
+        liveHost.add(new HostRef("123", "fluffhost1"));
+        liveHost.add(new HostRef("456", "fluffhost2"));
+        
+        Collection<HostRef> allHosts = new ArrayList<>();
+        allHosts.addAll(liveHost);
+        allHosts.add(new HostRef("789", "fluffhost3"));
+        
+        when(mockHostsDAO.getAliveHosts()).thenReturn(liveHost);
+        when(mockHostsDAO.getHosts()).thenReturn(allHosts);
+        
+        controller.doUpdateTreeAsync();
+        
+        ArgumentCaptor<HostsVMsLoader> arg = ArgumentCaptor.forClass(HostsVMsLoader.class);
+        verify(view).updateTree(anyString(), arg.capture());
+        HostsVMsLoader loader = arg.getValue();
+
+        Collection<HostRef> actualHosts = loader.getHosts();
+        assertEqualCollection(liveHost, actualHosts);
+        
+        l.actionPerformed(new ActionEvent<MainView.Action>(view, MainView.Action.SWITCH_HISTORY_MODE));
+        
+        actualHosts = loader.getHosts();
+        assertEqualCollection(allHosts, actualHosts);
+    }
+    
+    @Test
     public void verifyUpdateHostsVMsLoadsCorrectVMs() {
 
         Collection<VmRef> expectedVMs = new ArrayList<>();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/test/java/com/redhat/thermostat/client/SwingViewFactoryTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.SwingUtilities;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.client.ui.AgentConfigurationView;
+import com.redhat.thermostat.client.ui.ClientConfigurationView;
+import com.redhat.thermostat.client.ui.HostCpuView;
+import com.redhat.thermostat.client.ui.HostMemoryView;
+import com.redhat.thermostat.client.ui.HostOverviewView;
+import com.redhat.thermostat.client.ui.VmClassStatView;
+import com.redhat.thermostat.client.ui.VmCpuView;
+import com.redhat.thermostat.client.ui.VmGcView;
+import com.redhat.thermostat.client.ui.VmMemoryView;
+import com.redhat.thermostat.client.ui.VmOverviewView;
+import com.redhat.thermostat.common.View;
+
+public class SwingViewFactoryTest {
+
+    @Test
+    public void test() throws InvocationTargetException, InterruptedException {
+        SwingUtilities.invokeAndWait(new Runnable() {
+            @Override
+            public void run() {
+                SwingViewFactory factory = new SwingViewFactory();
+
+                List<Class<? extends View>> knownViewClasses = new ArrayList<>();
+
+                knownViewClasses.add(AgentConfigurationView.class);
+                knownViewClasses.add(ClientConfigurationView.class);
+                knownViewClasses.add(HostCpuView.class);
+                knownViewClasses.add(HostMemoryView.class);
+                knownViewClasses.add(HostOverviewView.class);
+                knownViewClasses.add(VmClassStatView.class);
+                knownViewClasses.add(VmCpuView.class);
+                knownViewClasses.add(VmGcView.class);
+                knownViewClasses.add(VmMemoryView.class);
+                knownViewClasses.add(VmOverviewView.class);
+
+                for (Class<? extends View> klass: knownViewClasses) {
+                    assertNotNull(factory.getViewClass(klass));
+                    assertNotNull(factory.getView(klass));
+                }
+            }
+
+        });
+
+    }
+}
--- a/client/src/test/java/com/redhat/thermostat/client/VmClassStatControllerTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/test/java/com/redhat/thermostat/client/VmClassStatControllerTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -37,6 +37,7 @@
 package com.redhat.thermostat.client;
 
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -49,6 +50,7 @@
 
 import com.redhat.thermostat.client.ui.VmClassStatController;
 import com.redhat.thermostat.client.ui.VmClassStatView;
+import com.redhat.thermostat.common.ViewFactory;
 import com.redhat.thermostat.common.appctx.ApplicationContext;
 import com.redhat.thermostat.common.dao.DAOFactory;
 import com.redhat.thermostat.common.dao.MongoDAOFactory;
@@ -58,6 +60,7 @@
 
 public class VmClassStatControllerTest {
 
+    @SuppressWarnings("unchecked") // any(List.class)
     @Test
     public void testChartUpdate() {
 
@@ -74,15 +77,13 @@
         ApplicationContext.getInstance().setDAOFactory(daoFactory);
         VmRef ref = mock(VmRef.class);
 
-        final VmClassStatView view = mock(VmClassStatView.class);
+        VmClassStatView view = mock(VmClassStatView.class);
+        ViewFactory viewFactory = mock(ViewFactory.class);
+        when(viewFactory.getView(eq(VmClassStatView.class))).thenReturn(view);
 
-        // TODO: Consider to pass the ClassesView or a factory for it to the controller instead.
-        VmClassStatController controller = new VmClassStatController(ref) {
-            @Override
-            protected VmClassStatView createView() {
-                return view;
-            }
-        };
+        ApplicationContext.getInstance().setViewFactory(viewFactory);
+
+        VmClassStatController controller = new VmClassStatController(ref);
 
         controller.start();
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/test/java/com/redhat/thermostat/client/config/ClientPreferencesTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.config;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import java.util.prefs.Preferences;
+
+import org.junit.Test;
+
+public class ClientPreferencesTest {
+
+    @Test
+    public void testGetConnectionUrl() {
+
+        Preferences prefs = mock(Preferences.class);
+        when(prefs.get(eq("connection-url"), any(String.class))).thenReturn("mock-value");
+
+        ClientPreferences clientPrefs = new ClientPreferences(prefs);
+        String value = clientPrefs.getConnectionUrl();
+
+        assertEquals("mock-value", value);
+        verify(prefs).get(eq("connection-url"), any(String.class));
+    }
+
+    @Test
+    public void testSetConnectionUrl() {
+
+        Preferences prefs = mock(Preferences.class);
+
+        ClientPreferences clientPrefs = new ClientPreferences(prefs);
+        clientPrefs.setConnectionUrl("test");
+
+        verify(prefs).put(eq("connection-url"), eq("test"));
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/test/java/com/redhat/thermostat/client/ui/AgentConfigurationControllerTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.ui;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import com.redhat.thermostat.client.ui.AgentConfigurationView.ConfigurationAction;
+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.appctx.ApplicationContext;
+import com.redhat.thermostat.common.appctx.ApplicationContextUtil;
+
+public class AgentConfigurationControllerTest {
+
+    @Before
+    public void setUp() {
+        ApplicationContextUtil.resetApplicationContext();
+
+        /*
+         * Set up a mock timer factory that always executes actions synchronously on start();
+         */
+        TimerFactory tf = mock(TimerFactory.class);
+        Timer timer = mock(Timer.class);
+        final Runnable[] runnable = new Runnable[1];
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                runnable[0] = (Runnable) invocation.getArguments()[0];
+                return null;
+            }
+
+        }).when(timer).setAction(any(Runnable.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                runnable[0].run();
+                return null;
+            }
+
+        }).when(timer).start();
+        when(tf.createTimer()).thenReturn(timer);
+        ApplicationContext.getInstance().setTimerFactory(tf);
+
+    }
+
+    @After
+    public void tearDown() {
+        ApplicationContextUtil.resetApplicationContext();
+    }
+
+    @Test
+    public void testAddingEnabledBackends() {
+        AgentConfigurationView view = mock(AgentConfigurationView.class);
+        AgentConfigurationModel model = mock(AgentConfigurationModel.class);
+        when(model.getAgents()).thenReturn(Arrays.asList(new String[]{"agent1"}));
+        List<String> backends = Arrays.asList(new String[] {"backend1", "backend2"});
+        when(model.getBackends(any(String.class))).thenReturn(backends);
+        when(model.getAgentBackendEnabled(any(String.class), any(String.class))).thenReturn(true);
+
+        Map<String,Boolean> expected = new HashMap<>();
+        expected.put("backend1", true);
+        expected.put("backend2", true);
+
+        AgentConfigurationController controller = new AgentConfigurationController(model, view);
+        controller.showView();
+        controller.hideView();
+
+        verify(view).addAgent("agent1");
+        verify(view).setBackendStatus(eq(expected));
+        verify(view).showDialog();
+        verify(view).hideDialog();
+    }
+
+    @Test
+    public void testAddingDisabledBackends() {
+        AgentConfigurationView view = mock(AgentConfigurationView.class);
+        AgentConfigurationModel model = mock(AgentConfigurationModel.class);
+        when(model.getAgents()).thenReturn(Arrays.asList(new String[]{"agent1"}));
+        List<String> backends = Arrays.asList(new String[] {"backend1",});
+        when(model.getBackends(any(String.class))).thenReturn(backends);
+        when(model.getAgentBackendEnabled(any(String.class), any(String.class))).thenReturn(false);
+
+        Map<String,Boolean> expected = new HashMap<>();
+        expected.put("backend1", false);
+
+        AgentConfigurationController controller = new AgentConfigurationController(model, view);
+        controller.showView();
+        controller.hideView();
+
+        verify(view).addAgent("agent1");
+        verify(view).setBackendStatus(eq(expected));
+        verify(view).showDialog();
+        verify(view).hideDialog();
+    }
+
+    /**
+     * Verify that the accepting the changes signals the controller
+     */
+    @SuppressWarnings("unchecked") // All this ActionListener fluff
+    @Test
+    public void testViewEditedBackends() {
+        final ActionListener<ConfigurationAction>[] listeners = (ActionListener<ConfigurationAction>[]) new ActionListener<?>[1];
+        AgentConfigurationView view = mock(AgentConfigurationView.class);
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                listeners[0] = (ActionListener<ConfigurationAction>) invocation.getArguments()[0];
+                return null;
+            }
+        }).when(view).addActionListener(any(ActionListener.class));
+
+
+        AgentConfigurationModel model = mock(AgentConfigurationModel.class);
+        when(model.getAgents()).thenReturn(Arrays.asList(new String[]{"agent1"}));
+        List<String> backends = Arrays.asList(new String[] {"backend1"});
+        when(model.getBackends(any(String.class))).thenReturn(backends);
+        when(model.getAgentBackendEnabled(any(String.class), any(String.class))).thenReturn(true);
+
+        Map<String,Boolean> expected = new HashMap<>();
+        expected.put("backend1", true);
+
+        AgentConfigurationController controller = new AgentConfigurationController(model, view);
+        controller.showView();
+
+        listeners[0].actionPerformed(new ActionEvent<ConfigurationAction>(view, ConfigurationAction.CLOSE_ACCEPT));
+
+        controller.hideView();
+
+        InOrder inOrder = inOrder(view);
+
+        inOrder.verify(view).addAgent("agent1");
+        inOrder.verify(view).setBackendStatus(eq(expected));
+        inOrder.verify(view).getBackendStatus();
+
+        verify(model).saveConfiguration();
+    }
+
+    /**
+     * Verify that controller handles cancel properly
+     */
+    @SuppressWarnings("unchecked") // All this ActionListener fluff.
+    @Test
+    public void testViewCancelEditingBackends() {
+        final ActionListener<ConfigurationAction>[] listeners = (ActionListener<ConfigurationAction>[]) new ActionListener<?>[1];
+        AgentConfigurationView view = mock(AgentConfigurationView.class);
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                listeners[0] = (ActionListener<ConfigurationAction>) invocation.getArguments()[0];
+                return null;
+            }
+        }).when(view).addActionListener(any(ActionListener.class));
+
+
+        AgentConfigurationModel model = mock(AgentConfigurationModel.class);
+        when(model.getAgents()).thenReturn(Arrays.asList(new String[]{"agent1"}));
+        List<String> backends = Arrays.asList(new String[] {"backend1"});
+        when(model.getBackends(any(String.class))).thenReturn(backends);
+        when(model.getAgentBackendEnabled(any(String.class), any(String.class))).thenReturn(true);
+
+        Map<String,Boolean> expectedConfig = new HashMap<>();
+        expectedConfig.put("backend1", true);
+
+        AgentConfigurationController controller = new AgentConfigurationController(model, view);
+        controller.showView();
+
+        listeners[0].actionPerformed(new ActionEvent<ConfigurationAction>(view, ConfigurationAction.CLOSE_CANCEL));
+
+        controller.hideView();
+
+        verify(view).addAgent("agent1");
+        verify(view).setBackendStatus(eq(expectedConfig));
+        verify(view, never()).getBackendStatus();
+
+        verify(model, never()).saveConfiguration();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/test/java/com/redhat/thermostat/client/ui/ClientConfigurationControllerTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.ui;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.client.config.ClientPreferences;
+import com.redhat.thermostat.common.ActionEvent;
+
+public class ClientConfigurationControllerTest {
+
+    @Test
+    public void verifyShowDialog() {
+        ClientPreferences model = mock(ClientPreferences.class);
+        when(model.getConnectionUrl()).thenReturn("mock-connection-url");
+
+        ClientConfigurationView view = mock(ClientConfigurationView.class);
+        ClientConfigurationController controller = new ClientConfigurationController(model, view);
+
+        controller.showDialog();
+
+        verify(model).getConnectionUrl();
+        verify(view).setConnectionUrl(eq("mock-connection-url"));
+        verify(view).showDialog();
+    }
+
+    @Test
+    public void verifyCloseCancel() {
+        ClientPreferences model = mock(ClientPreferences.class);
+        ClientConfigurationView view = mock(ClientConfigurationView.class);
+        ClientConfigurationController controller = new ClientConfigurationController(model, view);
+
+        controller.actionPerformed(new ActionEvent<>(view, ClientConfigurationView.Action.CLOSE_CANCEL));
+
+        verify(model, times(0)).setConnectionUrl(any(String.class));
+        verify(view, times(0)).getConnectionUrl();
+        verify(view, times(0)).showDialog();
+        verify(view).hideDialog();
+    }
+
+    @Test
+    public void verifyCloseAccept() {
+        ClientPreferences model = mock(ClientPreferences.class);
+        ClientConfigurationView view = mock(ClientConfigurationView.class);
+        when(view.getConnectionUrl()).thenReturn("mock-connection-url");
+        ClientConfigurationController controller = new ClientConfigurationController(model, view);
+
+        controller.actionPerformed(new ActionEvent<>(view, ClientConfigurationView.Action.CLOSE_ACCEPT));
+
+        verify(model).setConnectionUrl(eq("mock-connection-url"));
+        verify(view).getConnectionUrl();
+        verify(view, times(0)).showDialog();
+        verify(view).hideDialog();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/test/java/com/redhat/thermostat/client/ui/ClientConfigurationFrameTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.ui;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import org.fest.swing.annotation.GUITest;
+import org.fest.swing.edt.GuiActionRunner;
+import org.fest.swing.edt.GuiTask;
+import org.fest.swing.fixture.FrameFixture;
+import org.fest.swing.fixture.JButtonFixture;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+
+public class ClientConfigurationFrameTest {
+
+    private ClientConfigurationFrame frame;
+    private FrameFixture frameFixture;
+    private ActionListener<ClientConfigurationView.Action> l;
+
+    @SuppressWarnings("unchecked") // ActionListener
+    @Before
+    public void setUp() {
+        frame = new ClientConfigurationFrame();
+        l = mock(ActionListener.class);
+        frame.addListener(l);
+        frameFixture = new FrameFixture(frame);
+
+    }
+
+    @After
+    public void tearDown() {
+        frameFixture.cleanUp();
+        frame.removeListener(l);
+        frame = null;
+        l = null;
+    }
+
+    @Category(GUITest.class)
+    @Test
+    public void testOkayButton() {
+        frameFixture.show();
+
+        JButtonFixture button = frameFixture.button("ok");
+        button.click();
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                frame.hideDialog();
+            }
+        });
+
+        verify(l).actionPerformed(eq(new ActionEvent<>(frame, ClientConfigurationView.Action.CLOSE_ACCEPT)));
+
+
+    }
+
+    @Category(GUITest.class)
+    @Test
+    public void testCancelButton() {
+        frameFixture.show();
+
+        JButtonFixture button = frameFixture.button("cancel");
+        button.click();
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                frame.hideDialog();
+            }
+        });
+
+        verify(l).actionPerformed(eq(new ActionEvent<>(frame, ClientConfigurationView.Action.CLOSE_CANCEL)));
+
+    }
+}
--- a/client/src/test/java/com/redhat/thermostat/client/ui/HostCpuControllerTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/test/java/com/redhat/thermostat/client/ui/HostCpuControllerTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -38,6 +38,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.isNotNull;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
@@ -58,6 +59,7 @@
 import com.redhat.thermostat.common.Timer;
 import com.redhat.thermostat.common.Timer.SchedulingType;
 import com.redhat.thermostat.common.TimerFactory;
+import com.redhat.thermostat.common.ViewFactory;
 import com.redhat.thermostat.common.appctx.ApplicationContext;
 import com.redhat.thermostat.common.appctx.ApplicationContextUtil;
 import com.redhat.thermostat.common.dao.CpuStatDAO;
@@ -107,14 +109,13 @@
         ApplicationContext.getInstance().setDAOFactory(daoFactory);
 
         view = mock(HostCpuView.class);
+        ViewFactory viewFactory = mock(ViewFactory.class);
+        when(viewFactory.getView(eq(HostCpuView.class))).thenReturn(view);
+
+        ApplicationContext.getInstance().setViewFactory(viewFactory);
+
         HostRef host = new HostRef("123", "fluffhost");
-        controller = new HostCpuController(host) {
-            // TODO: Reverse dependency.
-            @Override
-            protected HostCpuView createView() {
-                return view;
-            }
-        };
+        controller = new HostCpuController(host);
 
         timerAction = actionCaptor.getValue();
     }
--- a/client/src/test/java/com/redhat/thermostat/client/ui/HostMemoryControllerTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/test/java/com/redhat/thermostat/client/ui/HostMemoryControllerTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -37,6 +37,7 @@
 package com.redhat.thermostat.client.ui;
 
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -45,9 +46,12 @@
 import java.util.LinkedList;
 import java.util.List;
 
+import org.junit.Before;
 import org.junit.Test;
 
+import com.redhat.thermostat.common.ViewFactory;
 import com.redhat.thermostat.common.appctx.ApplicationContext;
+import com.redhat.thermostat.common.appctx.ApplicationContextUtil;
 import com.redhat.thermostat.common.dao.DAOFactory;
 import com.redhat.thermostat.common.dao.HostInfoDAO;
 import com.redhat.thermostat.common.dao.HostRef;
@@ -58,6 +62,12 @@
 
 public class HostMemoryControllerTest {
 
+    @Before
+    public void setUp() {
+        ApplicationContextUtil.resetApplicationContext();
+    }
+
+    @SuppressWarnings("unchecked") // any(List.class)
     @Test
     public void testUpdate() {
         HostInfo hostInfo = new HostInfo("someHost", "someOS", "linux_0.0.1", "lreally_fast_cpu", 2, 1024);
@@ -76,14 +86,12 @@
         ApplicationContext.getInstance().setDAOFactory(daoFactory);
 
         HostRef ref = mock(HostRef.class);
-        final HostMemoryView view = mock(HostMemoryView.class);
-        // TODO: Consider to pass the ClassesView or a factory for it to the controller instead.
-        HostMemoryController controller = new HostMemoryController(ref) {
-            @Override
-            protected HostMemoryView createView() {
-                return view;
-            }
-        };
+        HostMemoryView view = mock(HostMemoryView.class);
+        ViewFactory viewFactory = mock(ViewFactory.class);
+        when(viewFactory.getView(eq(HostMemoryView.class))).thenReturn(view);
+        ApplicationContext.getInstance().setViewFactory(viewFactory);
+
+        HostMemoryController controller = new HostMemoryController(ref);
 
         controller.start();
 
--- a/client/src/test/java/com/redhat/thermostat/client/ui/MainWindowTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/test/java/com/redhat/thermostat/client/ui/MainWindowTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -42,22 +42,18 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import java.beans.PropertyChangeEvent;
-import java.util.Objects;
-
 import org.fest.swing.annotation.GUITest;
 import org.fest.swing.edt.GuiActionRunner;
 import org.fest.swing.edt.GuiTask;
 import org.fest.swing.fixture.FrameFixture;
+import org.fest.swing.fixture.JMenuItemFixture;
 import org.fest.swing.fixture.JTextComponentFixture;
 import org.fest.swing.junit.v4_5.runner.GUITestRunner;
-import org.hamcrest.Description;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
 
 import com.redhat.thermostat.client.ChangeableText;
 import com.redhat.thermostat.client.MainView;
@@ -69,34 +65,11 @@
 @RunWith(GUITestRunner.class)
 public class MainWindowTest {
 
-    private static class PropertyChangeEventMatcher extends ArgumentMatcher<PropertyChangeEvent> {
-
-        private PropertyChangeEvent event;
-
-        private PropertyChangeEventMatcher(PropertyChangeEvent ev) {
-            event = ev;
-        }
-
-        @Override
-        public boolean matches(Object argument) {
-            PropertyChangeEvent other = (PropertyChangeEvent) argument;
-            return event.getSource() == other.getSource()
-                    && Objects.equals(event.getPropertyName(), other.getPropertyName())
-                    && Objects.equals(event.getNewValue(), other.getNewValue())
-                    && Objects.equals(event.getOldValue(), other.getOldValue());
-        }
-
-        @Override
-        public void describeTo(Description description) {
-            super.describeTo(description);
-            description.appendText(event.getSource() + ", " + event.getPropertyName() + ", " + event.getOldValue() + ", " + event.getNewValue());
-        }
-    }
-
     private FrameFixture frameFixture;
     private MainWindow window;
     private ActionListener<MainView.Action> l;
 
+    @SuppressWarnings("unchecked") // mock(ActionListener.class)
     @Before
     public void setUp() {
 
@@ -147,7 +120,7 @@
     @Test
     public void verifyShowMainWindowShowsWindow() {
         GuiActionRunner.execute(new GuiTask() {
-            
+
             @Override
             protected void executeInEDT() throws Throwable {
                 window.showMainWindow();
@@ -158,6 +131,31 @@
 
     @Category(GUITest.class)
     @Test
+    public void verifyThatClientPreferencesMenuItemTriggersEvent() {
+        frameFixture.show();
+        JMenuItemFixture menuItem = frameFixture.menuItem("showClientConfig");
+        menuItem.click();
+        frameFixture.close();
+        frameFixture.requireNotVisible();
+
+        verify(l).actionPerformed(new ActionEvent<MainView.Action>(window, MainView.Action.SHOW_CLIENT_CONFIG));
+
+    }
+
+    @Category(GUITest.class)
+    @Test
+    public void verifyThatHistorySwitchTriggersEvent() {
+        frameFixture.show();
+        JMenuItemFixture menuItem = frameFixture.menuItem("historyModeSwitch");
+        menuItem.click();
+        frameFixture.close();
+        frameFixture.requireNotVisible();
+
+        verify(l).actionPerformed(new ActionEvent<MainView.Action>(window, MainView.Action.SWITCH_HISTORY_MODE));
+    }
+    
+    @Category(GUITest.class)
+    @Test
     public void testGetHostVMTreeFilter() {
         frameFixture.show();
         JTextComponentFixture hostVMTreeFilterField = frameFixture.textBox("hostVMTreeFilter");
@@ -166,5 +164,5 @@
         assertEquals("test", window.getHostVmTreeFilter());
     }
 
-    
+
 }
--- a/client/src/test/java/com/redhat/thermostat/client/ui/VmCpuControllerTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/client/src/test/java/com/redhat/thermostat/client/ui/VmCpuControllerTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -37,6 +37,7 @@
 package com.redhat.thermostat.client.ui;
 
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -45,9 +46,12 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import org.junit.Before;
 import org.junit.Test;
 
+import com.redhat.thermostat.common.ViewFactory;
 import com.redhat.thermostat.common.appctx.ApplicationContext;
+import com.redhat.thermostat.common.appctx.ApplicationContextUtil;
 import com.redhat.thermostat.common.dao.DAOFactory;
 import com.redhat.thermostat.common.dao.MongoDAOFactory;
 import com.redhat.thermostat.common.dao.VmCpuStatDAO;
@@ -57,6 +61,12 @@
 
 public class VmCpuControllerTest {
 
+    @Before
+    public void setUp() {
+        ApplicationContextUtil.resetApplicationContext();
+    }
+
+    @SuppressWarnings("unchecked") // any(List.class)
     @Test
     public void testChartUpdate() {
 
@@ -74,14 +84,12 @@
         VmRef ref = mock(VmRef.class);
 
         final VmCpuView view = mock(VmCpuView.class);
+        ViewFactory viewFactory = mock(ViewFactory.class);
+        when(viewFactory.getView(eq(VmCpuView.class))).thenReturn(view);
 
-        // TODO: Consider to pass the ClassesView or a factory for it to the controller instead.
-        VmCpuController controller = new VmCpuController(ref) {
-            @Override
-            protected VmCpuView createView() {
-                return view;
-            }
-        };
+        ApplicationContext.getInstance().setViewFactory(viewFactory);
+
+        VmCpuController controller = new VmCpuController(ref);
 
         controller.start();
 
--- a/common/src/main/java/com/redhat/thermostat/cli/AppContextSetupImpl.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/cli/AppContextSetupImpl.java	Mon Apr 16 15:41:08 2012 +0200
@@ -38,11 +38,11 @@
 
 import com.redhat.thermostat.common.appctx.ApplicationContext;
 import com.redhat.thermostat.common.config.StartupConfiguration;
-import com.redhat.thermostat.common.dao.Connection;
-import com.redhat.thermostat.common.dao.ConnectionProvider;
 import com.redhat.thermostat.common.dao.DAOFactory;
-import com.redhat.thermostat.common.dao.MongoConnectionProvider;
 import com.redhat.thermostat.common.dao.MongoDAOFactory;
+import com.redhat.thermostat.common.storage.Connection;
+import com.redhat.thermostat.common.storage.MongoStorageProvider;
+import com.redhat.thermostat.common.storage.StorageProvider;
 
 class AppContextSetupImpl implements AppContextSetup {
 
@@ -50,7 +50,7 @@
     public void setupAppContext(String dbUrl) {
         StartupConfiguration config = new ConnectionConfiguration(dbUrl);
         
-        ConnectionProvider connProv = new MongoConnectionProvider(config);
+        StorageProvider connProv = new MongoStorageProvider(config);
         DAOFactory daoFactory = new MongoDAOFactory(connProv);
         Connection connection = daoFactory.getConnection();
         connection.connect();
--- a/common/src/main/java/com/redhat/thermostat/cli/CommandException.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/cli/CommandException.java	Mon Apr 16 15:41:08 2012 +0200
@@ -38,6 +38,8 @@
 
 public class CommandException extends Exception {
 
+    private static final long serialVersionUID = 3730368617641245016L;
+
     public CommandException() {
         super();
     }
--- a/common/src/main/java/com/redhat/thermostat/common/ActionEvent.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/ActionEvent.java	Mon Apr 16 15:41:08 2012 +0200
@@ -40,6 +40,8 @@
 
 public class ActionEvent<T extends Enum<?>> extends EventObject {
 
+    private static final long serialVersionUID = -8648206929150142342L;
+
     public ActionEvent(Object source, T actionId) {
         super(source);
         if (actionId == null) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/common/View.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common;
+
+/**
+ * An object that is meant to be used as a View in an MVC setup.
+ *
+ * All implementations of this interface must have a no-arg public constructor
+ */
+public interface View {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/common/ViewFactory.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common;
+
+public interface ViewFactory {
+
+    public <T extends View> T getView(Class<T> viewClass);
+
+    public <T extends View> Class<? extends T> getViewClass(Class<T> viewClass);
+
+    public <T extends View > void setViewClass(Class<T> viewClass, Class<? extends T> implClass);
+}
--- a/common/src/main/java/com/redhat/thermostat/common/appctx/ApplicationContext.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/appctx/ApplicationContext.java	Mon Apr 16 15:41:08 2012 +0200
@@ -37,6 +37,7 @@
 package com.redhat.thermostat.common.appctx;
 
 import com.redhat.thermostat.common.TimerFactory;
+import com.redhat.thermostat.common.ViewFactory;
 import com.redhat.thermostat.common.dao.DAOFactory;
 
 public class ApplicationContext {
@@ -47,6 +48,8 @@
 
     private TimerFactory timerFactory;
 
+    private ViewFactory viewFactory;
+
     public static ApplicationContext getInstance() {
         return instance;
     }
@@ -76,4 +79,12 @@
         return timerFactory;
     }
 
+    public ViewFactory getViewFactory() {
+        return viewFactory;
+    }
+
+    public void setViewFactory(ViewFactory viewFactory) {
+        this.viewFactory = viewFactory;
+    }
+
 }
--- a/common/src/main/java/com/redhat/thermostat/common/config/InvalidConfigurationException.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/config/InvalidConfigurationException.java	Mon Apr 16 15:41:08 2012 +0200
@@ -39,6 +39,8 @@
 
 public class InvalidConfigurationException extends Exception {
 
+    private static final long serialVersionUID = -6555406006758264587L;
+
     public InvalidConfigurationException() {
         super();
     }
--- a/common/src/main/java/com/redhat/thermostat/common/dao/Connection.java	Mon Apr 16 14:23:46 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,133 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.common.dao;
-
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-public abstract class Connection {
-
-    public enum ConnectionType {
-        LOCAL(false),
-        REMOTE(true),
-        CLUSTER(true),
-        ;
-
-        boolean isDisplayable = false;
-        boolean needsUrl = false;
-
-        private ConnectionType(boolean needsUrl) {
-            this.needsUrl = needsUrl;
-        }
-
-        private ConnectionType(boolean isDisplayable, boolean needsUrl) {
-            this.isDisplayable = isDisplayable;
-        }
-
-        public boolean isDisplayable() {
-            return isDisplayable;
-        }
-
-        public boolean needsUrl() {
-            return needsUrl;
-        }
-    }
-
-    public enum ConnectionStatus {
-        CONNECTED,
-        CONNECTING,
-        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;
-    }
-
-    public ConnectionType getType() {
-        return type;
-    }
-
-    public void setUrl(String url) {
-        this.url = url;
-    }
-
-    public String getUrl() {
-        return url;
-    }
-
-    @Override
-    public String toString() {
-        if (url == null) {
-            return type.toString();
-        }
-        return type.toString() + " to " + url;
-    }
-
-    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);
-        }
-    }
-}
--- a/common/src/main/java/com/redhat/thermostat/common/dao/ConnectionProvider.java	Mon Apr 16 14:23:46 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.common.dao;
-
-public interface ConnectionProvider {
-
-    Connection createConnection();
-}
--- a/common/src/main/java/com/redhat/thermostat/common/dao/CpuStatDAOImpl.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/CpuStatDAOImpl.java	Mon Apr 16 15:41:08 2012 +0200
@@ -66,12 +66,12 @@
     }
 
     @Override
-    public long getCount() {
-        return storage.getCount(cpuStatCategory);
+    public void putCpuStat(CpuStat stat) {
+        storage.putChunk(converter.toChunk(stat));
     }
 
     @Override
-    public void putCpuStat(CpuStat stat) {
-        storage.putChunk(converter.toChunk(stat));
+    public long getCount() {
+        return storage.getCount(cpuStatCategory);
     }
 }
--- a/common/src/main/java/com/redhat/thermostat/common/dao/DAOFactory.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/DAOFactory.java	Mon Apr 16 15:41:08 2012 +0200
@@ -36,11 +36,14 @@
 
 package com.redhat.thermostat.common.dao;
 
+import com.redhat.thermostat.common.storage.Connection;
+import com.redhat.thermostat.common.storage.Storage;
 
 public interface DAOFactory {
-    /**
-     * TODO: This will be replaced by getStorage() as soon as Storage and Connection have been merged.
-     */
+
+    // TODO this is temporary until DAO is made for those that are still using Storage directly.
+    public Storage getStorage();
+
     public Connection getConnection();
 
     public HostInfoDAO getHostInfoDAO();
--- a/common/src/main/java/com/redhat/thermostat/common/dao/HostInfoDAO.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/HostInfoDAO.java	Mon Apr 16 15:41:08 2012 +0200
@@ -57,5 +57,8 @@
 
     HostInfo getHostInfo(HostRef ref);
 
+    void putHostInfo(HostInfo info);
+
     Collection<HostRef> getHosts();
+    Collection<HostRef> getAliveHosts();
 }
--- a/common/src/main/java/com/redhat/thermostat/common/dao/HostInfoDAOImpl.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/HostInfoDAOImpl.java	Mon Apr 16 15:41:08 2012 +0200
@@ -40,6 +40,7 @@
 import java.util.Collection;
 
 import com.redhat.thermostat.common.model.HostInfo;
+import com.redhat.thermostat.common.storage.AgentInformation;
 import com.redhat.thermostat.common.storage.Chunk;
 import com.redhat.thermostat.common.storage.Cursor;
 import com.redhat.thermostat.common.storage.Key;
@@ -63,9 +64,19 @@
     }
 
     @Override
+    public void putHostInfo(HostInfo info) {
+        storage.putChunk(converter.toChunk(info));
+    }
+    
+    @Override
     public Collection<HostRef> getHosts() {
+        return getHosts(new Chunk(hostInfoCategory, false));
+    }
+    
+    private Collection<HostRef> getHosts(Chunk filter) {
         Collection<HostRef> hosts = new ArrayList<HostRef>();
-        Cursor hostsCursor = storage.findAllFromCategory(hostInfoCategory);
+        
+        Cursor hostsCursor = storage.findAll(filter);
         while(hostsCursor.hasNext()) {
             Chunk hostChunk = hostsCursor.next();
             String agentId = hostChunk.get(Key.AGENT_ID);
@@ -74,7 +85,26 @@
         }
         return hosts;
     }
-
+    
+    @Override
+    public Collection<HostRef> getAliveHosts() {
+        
+        Collection<HostRef> hosts = new ArrayList<HostRef>();
+        
+        Chunk agents = new Chunk(AgentInformation.AGENT_INFO_CATEGORY, false);
+        agents.put(AgentInformation.AGENT_ALIVE_KEY, true);
+        Cursor agentCursor = storage.findAll(agents);
+        while(agentCursor.hasNext()) {
+            Chunk chunk = agentCursor.next();
+            
+            Chunk filter = new Chunk(hostInfoCategory, false);
+            filter.put(Key.AGENT_ID, chunk.get(Key.AGENT_ID));
+            
+            hosts.addAll(getHosts(filter));
+        }
+        
+        return hosts;
+    }
     @Override
     public long getCount() {
         return storage.getCount(hostInfoCategory);
--- a/common/src/main/java/com/redhat/thermostat/common/dao/MemoryStatDAO.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/MemoryStatDAO.java	Mon Apr 16 15:41:08 2012 +0200
@@ -57,4 +57,6 @@
             memoryCachedKey, memorySwapTotalKey, memorySwapFreeKey, memoryCommitLimitKey);
 
     public List<MemoryStat> getLatestMemoryStats(HostRef ref);
+
+    void putMemoryStat(MemoryStat stat);
 }
--- a/common/src/main/java/com/redhat/thermostat/common/dao/MemoryStatDAOImpl.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/MemoryStatDAOImpl.java	Mon Apr 16 15:41:08 2012 +0200
@@ -66,6 +66,11 @@
     }
 
     @Override
+    public void putMemoryStat(MemoryStat stat) {
+        storage.putChunk(converter.toChunk(stat));
+    }
+
+    @Override
     public long getCount() {
         return storage.getCount(memoryStatCategory);
     }
--- a/common/src/main/java/com/redhat/thermostat/common/dao/MongoConnection.java	Mon Apr 16 14:23:46 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,106 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.common.dao;
-
-import java.net.UnknownHostException;
-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.common.NotImplementedException;
-import com.redhat.thermostat.common.config.StartupConfiguration;
-import com.redhat.thermostat.common.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 StartupConfiguration conf;
-    
-    private MongoConnection() { /* nothing to do */ }
-    
-    public MongoConnection(StartupConfiguration conf) {
-        this.conf = conf;
-    }
-
-    @Override
-    public void connect() {
-        try {
-            createConnection();
-            /* the mongo java driver does not ensure this connection is actually working */
-            testConnection();
-        } catch (MongoException | UnknownHostException |
-                 NotImplementedException | IllegalArgumentException e)
-        {
-            fireChanged(ConnectionStatus.FAILED_TO_CONNECT);
-            return;
-        }
-        fireChanged(ConnectionStatus.CONNECTED);
-        connected = true;
-    }
-
-    private void createConnection() throws MongoException, UnknownHostException {
-        this.m = new Mongo(getMongoURI());
-        this.db = m.getDB(StorageConstants.THERMOSTAT_DB_NAME);
-    }
-    
-    private void testConnection() {
-        db.getCollection("agent-config").getCount();
-    }
-
-    private MongoURI getMongoURI() {
-        MongoURI uri = new MongoURI(conf.getDBConnectionString());
-        return uri;
-    }
-
-    public DB getDB() {
-        return db;
-    }
-
-    @Override
-    public void disconnect() {
-        if (m != null) {
-            m.close();
-        }
-        connected = false;
-    }
-}
--- a/common/src/main/java/com/redhat/thermostat/common/dao/MongoConnectionProvider.java	Mon Apr 16 14:23:46 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.common.dao;
-
-import com.redhat.thermostat.common.config.StartupConfiguration;
-
-public class MongoConnectionProvider implements ConnectionProvider {
-
-    private StartupConfiguration configuration;
-
-    public MongoConnectionProvider(StartupConfiguration configuration) {
-        this.configuration = configuration;
-    }
-
-    @Override
-    public Connection createConnection() {
-        return new MongoConnection(configuration);
-    }
-
-}
--- a/common/src/main/java/com/redhat/thermostat/common/dao/MongoDAOFactory.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/MongoDAOFactory.java	Mon Apr 16 15:41:08 2012 +0200
@@ -36,78 +36,84 @@
 
 package com.redhat.thermostat.common.dao;
 
-import com.redhat.thermostat.common.dao.Connection.ConnectionListener;
-import com.redhat.thermostat.common.dao.Connection.ConnectionStatus;
-import com.redhat.thermostat.common.storage.MongoStorage;
+import com.redhat.thermostat.common.storage.Connection;
+import com.redhat.thermostat.common.storage.StorageProvider;
 import com.redhat.thermostat.common.storage.Storage;
 
 public class MongoDAOFactory implements DAOFactory {
 
-    private final Connection connection;
     private final Storage storage;
 
-    public MongoDAOFactory(ConnectionProvider connProv) {
-
-        connection = connProv.createConnection();
-        final MongoStorage mongoStorage = new MongoStorage(connection);
-        storage = mongoStorage;
-        connection.addListener(new ConnectionListener() {
-
-            @Override
-            public void changed(ConnectionStatus newStatus) {
-                if (newStatus == ConnectionStatus.CONNECTED) {
-                    mongoStorage.connect(((MongoConnection) connection).getDB());
-                }
-            }
-        });
+    public MongoDAOFactory(StorageProvider prov) {
+        storage = prov.createStorage();
     }
 
     @Override
     public Connection getConnection() {
-        return connection;
+        return storage.getConnection();
     }
 
     @Override
     public HostInfoDAO getHostInfoDAO() {
+        ensureStorageConnected();
         return new HostInfoDAOImpl(storage);
     }
 
     @Override
     public CpuStatDAO getCpuStatDAO() {
+        ensureStorageConnected();
         return new CpuStatDAOImpl(storage);
     }
 
     @Override
     public MemoryStatDAO getMemoryStatDAO() {
+        ensureStorageConnected();
         return new MemoryStatDAOImpl(storage);
     }
 
     @Override
     public NetworkInterfaceInfoDAO getNetworkInterfaceInfoDAO() {
+        ensureStorageConnected();
         return new NetworkInterfaceInfoDAOImpl(storage);
     }
 
     @Override
     public VmInfoDAO getVmInfoDAO() {
+        ensureStorageConnected();
         return new VmInfoDAOImpl(storage);
     }
 
     @Override
     public VmCpuStatDAO getVmCpuStatDAO() {
+        ensureStorageConnected();
         return new VmCpuStatDAOImpl(storage);
     }
 
     public VmMemoryStatDAO getVmMemoryStatDAO() {
+        ensureStorageConnected();
         return new VmMemoryStatDAOImpl(storage);
     }
 
     @Override
     public VmClassStatDAO getVmClassStatsDAO() {
+        ensureStorageConnected();
         return new VmClassStatDAOImpl(storage);
     }
 
     @Override
     public VmGcStatDAO getVmGcStatDAO() {
+        ensureStorageConnected();
         return new VmGcStatDAOImpl(storage);
     }
+
+    @Override
+    public Storage getStorage() {
+        return storage;
+    }
+
+    private void ensureStorageConnected() {
+        if (!storage.getConnection().isConnected()) {
+            throw new IllegalStateException("Set up connection before accessing DAO");
+        }
+    }
 }
--- a/common/src/main/java/com/redhat/thermostat/common/dao/NetworkInterfaceInfoDAO.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/NetworkInterfaceInfoDAO.java	Mon Apr 16 15:41:08 2012 +0200
@@ -52,4 +52,6 @@
             Key.AGENT_ID, Key.TIMESTAMP, ifaceKey, ip4AddrKey, ip6AddrKey);
 
     public List<NetworkInterfaceInfo> getNetworkInterfaces(HostRef ref);
+
+    public void putNetworkInterfaceInfo(NetworkInterfaceInfo info);
 }
--- a/common/src/main/java/com/redhat/thermostat/common/dao/NetworkInterfaceInfoDAOImpl.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/NetworkInterfaceInfoDAOImpl.java	Mon Apr 16 15:41:08 2012 +0200
@@ -48,9 +48,11 @@
 class NetworkInterfaceInfoDAOImpl implements NetworkInterfaceInfoDAO {
 
     private Storage storage;
+    private NetworkInterfaceInfoConverter converter;
 
     NetworkInterfaceInfoDAOImpl(Storage storage) {
         this.storage = storage;
+        converter = new NetworkInterfaceInfoConverter();
     }
 
     @Override
@@ -59,7 +61,6 @@
         query.put(Key.AGENT_ID, ref.getAgentId());
 
         Cursor cursor = storage.findAll(query);
-        NetworkInterfaceInfoConverter converter = new NetworkInterfaceInfoConverter();
         List<NetworkInterfaceInfo> result = new ArrayList<>();
         while (cursor.hasNext()) {
             Chunk chunk = cursor.next();
@@ -69,4 +70,9 @@
         return result;
     }
 
+    @Override
+    public void putNetworkInterfaceInfo(NetworkInterfaceInfo info) {
+        storage.putChunk(converter.toChunk(info));
+    }
+
 }
--- a/common/src/main/java/com/redhat/thermostat/common/dao/VmClassStatDAO.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/VmClassStatDAO.java	Mon Apr 16 15:41:08 2012 +0200
@@ -51,4 +51,6 @@
 
     public List<VmClassStat> getLatestClassStats(VmRef ref);
 
+    public void putVmClassStat(VmClassStat stat);
+
 }
\ No newline at end of file
--- a/common/src/main/java/com/redhat/thermostat/common/dao/VmClassStatDAOImpl.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/VmClassStatDAOImpl.java	Mon Apr 16 15:41:08 2012 +0200
@@ -64,4 +64,9 @@
         }
         return getter.getLatest();
     }
+
+    @Override
+    public void putVmClassStat(VmClassStat stat) {
+        storage.putChunk(converter.toChunk(stat));
+    }
 }
--- a/common/src/main/java/com/redhat/thermostat/common/dao/VmCpuStatDAO.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/VmCpuStatDAO.java	Mon Apr 16 15:41:08 2012 +0200
@@ -51,4 +51,6 @@
 
     public abstract List<VmCpuStat> getLatestVmCpuStats(VmRef ref);
 
+    public abstract void putVmCpuStat(VmCpuStat stat);
+
 }
--- a/common/src/main/java/com/redhat/thermostat/common/dao/VmCpuStatDAOImpl.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/VmCpuStatDAOImpl.java	Mon Apr 16 15:41:08 2012 +0200
@@ -64,4 +64,9 @@
         }
         return getter.getLatest();
     }
+
+    @Override
+    public void putVmCpuStat(VmCpuStat stat) {
+        storage.putChunk(converter.toChunk(stat));
+    }
 }
--- a/common/src/main/java/com/redhat/thermostat/common/dao/VmGcStatDAO.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/VmGcStatDAO.java	Mon Apr 16 15:41:08 2012 +0200
@@ -54,4 +54,6 @@
             runCountKey, wallTimeKey);
 
     public List<VmGcStat> getLatestVmGcStats(VmRef ref);
+
+    public void putVmGcStat(VmGcStat stat);
 }
--- a/common/src/main/java/com/redhat/thermostat/common/dao/VmGcStatDAOImpl.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/VmGcStatDAOImpl.java	Mon Apr 16 15:41:08 2012 +0200
@@ -65,4 +65,9 @@
         return getter.getLatest();
     }
 
+    @Override
+    public void putVmGcStat(VmGcStat stat) {
+        storage.putChunk(converter.toChunk(stat));
+    }
+
 }
--- a/common/src/main/java/com/redhat/thermostat/common/dao/VmInfoDAO.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/VmInfoDAO.java	Mon Apr 16 15:41:08 2012 +0200
@@ -72,4 +72,8 @@
     public VmInfo getVmInfo(VmRef ref);
 
     Collection<VmRef> getVMs(HostRef host);
+
+    public void putVmInfo(VmInfo info);
+
+    public void putVmStoppedTime(int vmId, long timestamp);
 }
--- a/common/src/main/java/com/redhat/thermostat/common/dao/VmInfoDAOImpl.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/VmInfoDAOImpl.java	Mon Apr 16 15:41:08 2012 +0200
@@ -102,4 +102,21 @@
     public long getCount() {
         return storage.getCount(vmInfoCategory);
     }
+
+    @Override
+    public void putVmInfo(VmInfo info) {
+        storage.putChunk(converter.toChunk(info));
+    }
+
+    @Override
+    public void putVmStoppedTime(int vmId, long timestamp) {
+        storage.updateChunk(makeStoppedChunk(vmId, timestamp));
+    }
+
+    private Chunk makeStoppedChunk(int vmId, long stopTimeStamp) {
+        Chunk chunk = new Chunk(VmInfoDAO.vmInfoCategory, false);
+        chunk.put(VmInfoDAO.vmIdKey, vmId);
+        chunk.put(VmInfoDAO.stopTimeKey, stopTimeStamp);
+        return chunk;
+    }
 }
--- a/common/src/main/java/com/redhat/thermostat/common/dao/VmMemoryStatDAO.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/VmMemoryStatDAO.java	Mon Apr 16 15:41:08 2012 +0200
@@ -87,4 +87,6 @@
 
     public VmMemoryStat getLatestMemoryStat(VmRef ref);
 
+    public void putVmMemoryStat(VmMemoryStat stat);
+
 }
--- a/common/src/main/java/com/redhat/thermostat/common/dao/VmMemoryStatDAOImpl.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/VmMemoryStatDAOImpl.java	Mon Apr 16 15:41:08 2012 +0200
@@ -45,9 +45,11 @@
 class VmMemoryStatDAOImpl implements VmMemoryStatDAO {
 
     private final Storage storage;
+    private final VmMemoryStatConverter converter;
 
     VmMemoryStatDAOImpl(Storage storage) {
         this.storage = storage;
+        converter = new VmMemoryStatConverter();
     }
 
     @Override
@@ -57,9 +59,14 @@
         query.put(Key.VM_ID, ref.getId());
         Cursor cursor = storage.findAll(query).sort(Key.TIMESTAMP, Cursor.SortDirection.DESCENDING).limit(1);
         if (cursor.hasNext()) {
-            return new VmMemoryStatConverter().fromChunk(cursor.next());
+            return converter.fromChunk(cursor.next());
         }
         return null;
     }
 
+    @Override
+    public void putVmMemoryStat(VmMemoryStat stat) {
+        storage.putChunk(converter.toChunk(stat));
+    }
+
 }
--- a/common/src/main/java/com/redhat/thermostat/common/storage/AgentInformation.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/storage/AgentInformation.java	Mon Apr 16 15:41:08 2012 +0200
@@ -42,9 +42,18 @@
 
 public class AgentInformation {
 
+    public static final Category AGENT_INFO_CATEGORY =
+            new Category(StorageConstants.CATEGORY_AGENT_CONFIG, Key.AGENT_ID);
+
+    public static final Key<Boolean> AGENT_ALIVE_KEY = new Key<>("alive", false);
+    
     private long startTime;
+    private long stopTime;
+
+    private boolean alive;
+    
     private List<BackendInformation> backends = new ArrayList<BackendInformation>();
-
+    
     public long getStartTime() {
         return startTime;
     }
@@ -53,10 +62,26 @@
         this.startTime = startTime;
     }
 
+    public void setStopTime(long stopTime) {
+        this.stopTime = stopTime;
+    }
+    
+    public long getStopTime() {
+        return stopTime;
+    }
+    
     public List<BackendInformation> getBackends() {
         return Collections.unmodifiableList(backends);
     }
 
+    public boolean isAlive() {
+        return alive;
+    }
+    
+    public void setAlive(boolean alive) {
+        this.alive = alive;
+    }
+    
     public void addBackend(BackendInformation backend) {
         backends.add(backend);
     }
--- a/common/src/main/java/com/redhat/thermostat/common/storage/ChunkConverter.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/storage/ChunkConverter.java	Mon Apr 16 15:41:08 2012 +0200
@@ -117,12 +117,14 @@
         }
     }
 
+    @SuppressWarnings("unchecked")
     private void dbObjectToChunkRecursively(Chunk chunk, DBObject dbObject, Category category, String dbKey, String fullKey) {
         Object value = dbObject.get(dbKey);
         if (value instanceof DBObject) {
             DBObject dbObj = (DBObject) value;
             dbObjectToChunkRecurse(chunk, dbObj, category, fullKey);
         } else {
+            @SuppressWarnings("rawtypes")
             Key key = category.getKey(fullKey);
             if (key != null) {
                 chunk.put(key, value);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/common/storage/Connection.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common.storage;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+public abstract class Connection {
+
+    public enum ConnectionType {
+        LOCAL(false),
+        REMOTE(true),
+        CLUSTER(true),
+        ;
+
+        boolean isDisplayable = false;
+        boolean needsUrl = false;
+
+        private ConnectionType(boolean needsUrl) {
+            this.needsUrl = needsUrl;
+        }
+
+        private ConnectionType(boolean isDisplayable, boolean needsUrl) {
+            this.isDisplayable = isDisplayable;
+        }
+
+        public boolean isDisplayable() {
+            return isDisplayable;
+        }
+
+        public boolean needsUrl() {
+            return needsUrl;
+        }
+    }
+
+    public enum ConnectionStatus {
+        CONNECTED,
+        CONNECTING,
+        FAILED_TO_CONNECT,
+        DISCONNECTED,
+    }
+
+    public interface ConnectionListener {
+        public void changed(ConnectionStatus newStatus);
+    }
+
+    protected boolean connected = false;
+
+    private ConnectionType type;
+    private String url;
+
+    private List<Connection.ConnectionListener> listeners = new CopyOnWriteArrayList<>();
+
+    public void setType(ConnectionType type) {
+        this.type = type;
+    }
+
+    public ConnectionType getType() {
+        return type;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    @Override
+    public String toString() {
+        if (url == null) {
+            return type.toString();
+        }
+        return type.toString() + " to " + url;
+    }
+
+    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);
+        }
+    }
+}
--- a/common/src/main/java/com/redhat/thermostat/common/storage/ConnectionFailedException.java	Mon Apr 16 14:23:46 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.common.storage;
-
-public class ConnectionFailedException extends Exception {
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/common/storage/MongoConnection.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common.storage;
+
+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.common.NotImplementedException;
+import com.redhat.thermostat.common.config.StartupConfiguration;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+
+class MongoConnection extends Connection {
+
+    private static final Logger logger = LoggingUtils.getLogger(MongoConnection.class);
+    private Mongo m = null;
+    private DB db = null;
+    private StartupConfiguration conf;
+
+    MongoConnection(StartupConfiguration conf) {
+        this.conf = conf;
+    }
+
+    @Override
+    public void connect() {
+        try {
+            createConnection();
+            /* the mongo java driver does not ensure this connection is actually working */
+            testConnection();
+        } catch (MongoException | UnknownHostException |
+                 NotImplementedException | IllegalArgumentException e)
+        {
+            logger.log(Level.WARNING, "Connection failed.", e);
+            fireChanged(ConnectionStatus.FAILED_TO_CONNECT);
+            return;
+        }
+        fireChanged(ConnectionStatus.CONNECTED);
+        connected = true;
+    }
+
+    @Override
+    public void disconnect() {
+        connected = false;
+        db = null;
+        if (m != null) {
+            m.close();
+        }
+    }
+
+    public DB getDB() {
+        return db;
+    }
+
+    private void createConnection() throws MongoException, UnknownHostException {
+        this.m = new Mongo(getMongoURI());
+        this.db = m.getDB(StorageConstants.THERMOSTAT_DB_NAME);
+    }
+
+    private MongoURI getMongoURI() {
+        MongoURI uri = new MongoURI(conf.getDBConnectionString());
+        return uri;
+    }
+
+    private void testConnection() {
+        db.getCollection("agent-config").getCount();
+    }
+}
--- a/common/src/main/java/com/redhat/thermostat/common/storage/MongoStorage.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/storage/MongoStorage.java	Mon Apr 16 15:41:08 2012 +0200
@@ -50,8 +50,9 @@
 import com.mongodb.DBCursor;
 import com.mongodb.DBObject;
 import com.mongodb.WriteConcern;
-import com.redhat.thermostat.common.dao.Connection;
-import com.redhat.thermostat.common.dao.MongoConnection;
+import com.redhat.thermostat.common.config.StartupConfiguration;
+import com.redhat.thermostat.common.storage.Connection.ConnectionListener;
+import com.redhat.thermostat.common.storage.Connection.ConnectionStatus;
 
 /**
  * Implementation of the Storage interface that uses MongoDB to store the instrumentation data.
@@ -60,32 +61,35 @@
  */
 public class MongoStorage extends Storage {
 
-    public MongoStorage(Connection connection) {
-        super(connection);
-    }
-
     public static final String KEY_AGENT_ID = "agent-id";
     public static final String SET_MODIFIER = "$set";
 
+    private MongoConnection conn;
     private DB db = null;
     private Map<String, DBCollection> collectionCache = new HashMap<String, DBCollection>();
 
     private UUID agentId = null;
 
-    @Override
-    public void connect() throws ConnectionFailedException {
-        connection.connect();
-        db = ((MongoConnection) connection).getDB();
+    public MongoStorage(StartupConfiguration conf) {
+        conn = new MongoConnection(conf);
+        conn.addListener(new ConnectionListener() {
+            @Override
+            public void changed(ConnectionStatus newStatus) {
+                switch (newStatus) {
+                case DISCONNECTED:
+                    db = null;
+                case CONNECTED:
+                    db = conn.getDB();
+                default:
+                    // ignore other status events
+                }
+            }
+        });
     }
 
-    /**
-     * Connects to an already existing connection. TODO: This is here for compatibility with the Connection class
-     * until they have been merged.
-     *
-     * @param db
-     */
-    public void connect(DB db) {
-        this.db = db;
+    @Override
+    public Connection getConnection() {
+        return conn;
     }
 
     @Override
@@ -93,35 +97,6 @@
         this.agentId = agentId;
     }
 
-    @Override
-    public void addAgentInformation(AgentInformation agentInfo) {
-        DBCollection configCollection = db.getCollection(StorageConstants.CATEGORY_AGENT_CONFIG);
-        DBObject toInsert = createConfigDBObject(agentInfo);
-        /* cast required to disambiguate between putAll(BSONObject) and putAll(Map) */
-        toInsert.putAll((BSONObject) getAgentDBObject());
-        configCollection.insert(toInsert, WriteConcern.SAFE);
-    }
-
-    @Override
-    public void removeAgentInformation() {
-        DBCollection configCollection = db.getCollection(StorageConstants.CATEGORY_AGENT_CONFIG);
-        BasicDBObject toRemove = getAgentDBObject();
-        configCollection.remove(toRemove, WriteConcern.NORMAL);
-    }
-
-    @Override
-    public String getBackendConfig(String backendName, String configurationKey) {
-        DBCollection configCollection = db.getCollection(StorageConstants.CATEGORY_AGENT_CONFIG);
-        BasicDBObject query = getAgentDBObject();
-        query.put(StorageConstants.KEY_AGENT_CONFIG_BACKENDS + "." + backendName, new BasicDBObject("$exists", true));
-        DBObject config = configCollection.findOne(query);
-        Object value = config.get(configurationKey);
-        if (value instanceof String) {
-            return (String) value;
-        }
-        return null;
-    }
-
     private BasicDBObject getAgentDBObject() {
         return new BasicDBObject(KEY_AGENT_ID, agentId.toString());
     }
@@ -259,11 +234,15 @@
     private DBObject createConfigDBObject(AgentInformation agentInfo) {
         BasicDBObject result = getAgentDBObject();
         result.put(StorageConstants.KEY_AGENT_CONFIG_AGENT_START_TIME, agentInfo.getStartTime());
+        result.put(StorageConstants.KEY_AGENT_CONFIG_AGENT_STOP_TIME, agentInfo.getStopTime());
+        result.put(StorageConstants.KEY_AGENT_CONFIG_AGENT_ALIVE, agentInfo.isAlive());
+        
         BasicDBObject backends = new BasicDBObject();
         for (BackendInformation backend : agentInfo.getBackends()) {
             backends.put(backend.getName(), createBackendConfigDBObject(backend));
         }
         result.put(StorageConstants.KEY_AGENT_CONFIG_BACKENDS, backends);
+        
         return result;
     }
 
@@ -287,13 +266,14 @@
         return result;
     }
 
+    @Override
     public void purge() {
         BasicDBObject deleteKey = getAgentDBObject();
         for (DBCollection coll : collectionCache.values()) {
             coll.remove(deleteKey);
         }
     }
-
+    
     @Override
     public ConnectionKey createConnectionKey(Category category) {
         // TODO: There is probably some better place to do this, perhaps related to the inner class
@@ -311,7 +291,8 @@
         Category cat = query.getCategory();
         DBCollection coll = getCachedCollection(cat.getName());
         ChunkConverter converter = new ChunkConverter();
-        DBCursor dbCursor = coll.find(converter.chunkToDBObject(query));
+        DBObject obj = converter.chunkToDBObject(query);
+        DBCursor dbCursor = coll.find(obj);
         return new MongoCursor(dbCursor, query.getCategory());
     }
 
@@ -330,7 +311,7 @@
         DBObject dbResult = coll.findOne(converter.chunkToDBObject(query));
         return dbResult == null ? null : converter.dbObjectToChunk(dbResult, cat);
     }
-
+    
     @Override
     public Cursor findAllFromCategory(Category category) {
         DBCollection coll = getCachedCollection(category.getName());
@@ -346,4 +327,45 @@
         }
         return 0L;
     }
+
+    // TODO these methods below belong in some DAO.
+    @Override
+    public void addAgentInformation(AgentInformation agentInfo) {
+        DBCollection configCollection = db.getCollection(StorageConstants.CATEGORY_AGENT_CONFIG);
+        DBObject toInsert = createConfigDBObject(agentInfo);
+        /* cast required to disambiguate between putAll(BSONObject) and putAll(Map) */
+        toInsert.putAll((BSONObject) getAgentDBObject());
+        configCollection.insert(toInsert, WriteConcern.SAFE);
+    }
+    
+    @Override
+    public void updateAgentInformation(AgentInformation agentInfo) {
+        BasicDBObject queryObject = getAgentDBObject();
+
+        DBObject updated = createConfigDBObject(agentInfo);
+        updated.putAll((BSONObject) queryObject);
+
+        DBCollection configCollection = db.getCollection(StorageConstants.CATEGORY_AGENT_CONFIG);
+        configCollection.update(queryObject, updated);
+    }
+
+    @Override
+    public void removeAgentInformation() {
+        DBCollection configCollection = db.getCollection(StorageConstants.CATEGORY_AGENT_CONFIG);
+        BasicDBObject toRemove = getAgentDBObject();
+        configCollection.remove(toRemove, WriteConcern.NORMAL);
+    }
+
+    @Override
+    public String getBackendConfig(String backendName, String configurationKey) {
+        DBCollection configCollection = db.getCollection(StorageConstants.CATEGORY_AGENT_CONFIG);
+        BasicDBObject query = getAgentDBObject();
+        query.put(StorageConstants.KEY_AGENT_CONFIG_BACKENDS + "." + backendName, new BasicDBObject("$exists", true));
+        DBObject config = configCollection.findOne(query);
+        Object value = config.get(configurationKey);
+        if (value instanceof String) {
+            return (String) value;
+        }
+        return null;
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/common/storage/MongoStorageProvider.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common.storage;
+
+import com.redhat.thermostat.common.config.StartupConfiguration;
+
+public class MongoStorageProvider implements StorageProvider {
+
+    private StartupConfiguration configuration;
+
+    public MongoStorageProvider(StartupConfiguration configuration) {
+        this.configuration = configuration;
+    }
+
+    @Override
+    public Storage createStorage() {
+        return new MongoStorage(configuration);
+    }
+
+}
--- a/common/src/main/java/com/redhat/thermostat/common/storage/Storage.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/storage/Storage.java	Mon Apr 16 15:41:08 2012 +0200
@@ -38,29 +38,11 @@
 
 import java.util.UUID;
 
-import com.redhat.thermostat.common.dao.Connection;
 
 public abstract class Storage {
 
-    protected Connection connection;
-    
-    public Storage (Connection connection) {
-        this.connection = connection;
-    }
-    
-    public abstract void connect() throws ConnectionFailedException;
-
     public abstract void setAgentId(UUID id);
 
-    public abstract void addAgentInformation(AgentInformation agentInfo);
-
-    public abstract void removeAgentInformation();
-
-    /**
-     * @return {@code null} if the value is invalid or missing
-     */
-    public abstract String getBackendConfig(String backendName, String configurationKey);
-
     public final void registerCategory(Category category) {
         if (category.hasBeenRegistered()) {
             throw new IllegalStateException("Category may only be associated with one backend.");
@@ -69,21 +51,37 @@
         category.setConnectionKey(connKey);
     }
 
+    public abstract Connection getConnection();
+
     public abstract ConnectionKey createConnectionKey(Category category);
 
     public abstract void putChunk(Chunk chunk);
 
     public abstract void updateChunk(Chunk chunk);
 
-    /* Drop all data related to the currently running agent.
+    /**
+     * Drop all data related to the currently running agent.
      */
     public abstract void purge();
-
+    
     public abstract Cursor findAll(Chunk query);
 
     public abstract Chunk find(Chunk query);
 
     public abstract Cursor findAllFromCategory(Category category);
-
+    
     public abstract long getCount(Category category);
+
+    // TODO these will move to appropriate DAO
+    public abstract void addAgentInformation(AgentInformation agentInfo);
+
+    public abstract void removeAgentInformation();
+
+    public abstract void updateAgentInformation(AgentInformation agentInfo);
+
+    /**
+     * @return {@code null} if the value is invalid or missing
+     */
+    public abstract String getBackendConfig(String backendName, String configurationKey);
+
 }
--- a/common/src/main/java/com/redhat/thermostat/common/storage/StorageConstants.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/storage/StorageConstants.java	Mon Apr 16 15:41:08 2012 +0200
@@ -48,4 +48,7 @@
     public static final String KEY_AGENT_CONFIG_BACKEND_ACTIVE = "active";
     public static final String KEY_AGENT_CONFIG_BACKEND_NEW = "new";
     public static final String KEY_AGENT_CONFIG_BACKEND_PIDS = "pids";
+
+    public static final String KEY_AGENT_CONFIG_AGENT_ALIVE = "alive";
+    public static final String KEY_AGENT_CONFIG_AGENT_STOP_TIME = "stop-time";
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/common/storage/StorageProvider.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common.storage;
+
+public interface StorageProvider {
+
+    Storage createStorage();
+}
--- a/common/src/main/java/com/redhat/thermostat/tools/ApplicationException.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/tools/ApplicationException.java	Mon Apr 16 15:41:08 2012 +0200
@@ -38,6 +38,8 @@
 
 public class ApplicationException extends Exception {
 
+    private static final long serialVersionUID = 5910852125476383826L;
+
     public ApplicationException() {
     }
 
--- a/common/src/test/java/com/redhat/thermostat/cli/AppContextSetupImplTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.cli;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.Mockito.mock;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mockito;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
-
-import com.redhat.thermostat.cli.AppContextSetupImpl;
-import com.redhat.thermostat.common.appctx.ApplicationContext;
-import com.redhat.thermostat.common.config.StartupConfiguration;
-import com.redhat.thermostat.common.dao.MongoConnection;
-import com.redhat.thermostat.common.dao.MongoConnectionProvider;
-
-@RunWith(PowerMockRunner.class)
-@PrepareForTest(MongoConnectionProvider.class)
-public class AppContextSetupImplTest {
-
-    @Test
-    public void testSetup() throws Exception {
-        // TODO: Figure out how to test this class without using PowerMock.
-        final MongoConnection conn = mock(MongoConnection.class);
-        PowerMockito.whenNew(MongoConnection.class).withArguments(Mockito.any(StartupConfiguration.class)).thenAnswer(new Answer<MongoConnection>() {
-
-            @Override
-            public MongoConnection answer(InvocationOnMock invocation)
-                    throws Throwable {
-                StartupConfiguration conf = (StartupConfiguration) invocation.getArguments()[0];
-                assertEquals("mongodb://fluff:27518", conf.getDBConnectionString());
-                return conn;
-            }
-            
-        });
-        AppContextSetupImpl setup = new AppContextSetupImpl();
-
-        setup.setupAppContext("mongodb://fluff:27518");
-
-        assertNotNull(ApplicationContext.getInstance().getDAOFactory());
-    }
-
-}
--- a/common/src/test/java/com/redhat/thermostat/common/dao/ConnectionTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,112 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.common.dao;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.redhat.thermostat.common.dao.Connection.ConnectionListener;
-import com.redhat.thermostat.common.dao.Connection.ConnectionStatus;
-
-public class ConnectionTest {
-
-    private Connection connection;
-
-    private ConnectionListener listener1;
-    private ConnectionListener listener2;
-
-    @Before
-    public void setUp() {
-
-        connection = new Connection() {
-            
-            @Override
-            public void disconnect() {
-                // TODO Auto-generated method stub
-                
-            }
-            
-            @Override
-            public void connect() {
-                // TODO Auto-generated method stub
-                
-            }
-        };
-        listener1 = mock(ConnectionListener.class);
-        listener2 = mock(ConnectionListener.class);
-        connection.addListener(listener1);
-        connection.addListener(listener2);
-    }
-
-    @After
-    public void tearDown() {
-        connection = null;
-        listener1 = null;
-        listener2 = null;
-    }
-
-    @Test
-    public void testListenersConnecting() throws Exception {
-        verifyListenersStatus(ConnectionStatus.CONNECTING);
-    }
-
-    @Test
-    public void testListenersConnected() throws Exception {
-        verifyListenersStatus(ConnectionStatus.CONNECTED);
-    }
-
-    @Test
-    public void testListenersFailedToConnect() throws Exception {
-        verifyListenersStatus(ConnectionStatus.FAILED_TO_CONNECT);
-    }
-
-    @Test
-    public void testListenersDisconnected() throws Exception {
-        verifyListenersStatus(ConnectionStatus.DISCONNECTED);
-    }
-
-    private void verifyListenersStatus(ConnectionStatus status) {
-        connection.fireChanged(status);
-        verify(listener1).changed(status);
-        verify(listener2).changed(status);
-    }
-
-}
--- a/common/src/test/java/com/redhat/thermostat/common/dao/CpuStatDAOTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/test/java/com/redhat/thermostat/common/dao/CpuStatDAOTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -139,6 +139,16 @@
         CpuStat stat = new CpuStat(1, 5.0, 15.0, 10.0);
         CpuStatDAO dao = new CpuStatDAOImpl(storage);
         dao.putCpuStat(stat);
+
+        ArgumentCaptor<Chunk> arg = ArgumentCaptor.forClass(Chunk.class);
+        verify(storage).putChunk(arg.capture());
+        Chunk chunk = arg.getValue();
+
+        assertEquals(CpuStatDAO.cpuStatCategory, chunk.getCategory());
+        assertEquals((Long) 1L, chunk.get(Key.TIMESTAMP));
+        assertEquals((Double) 5.0, chunk.get(CpuStatDAO.cpu5LoadKey));
+        assertEquals((Double) 15.0, chunk.get(CpuStatDAO.cpu10LoadKey));
+        assertEquals((Double) 10.0, chunk.get(CpuStatDAO.cpu15LoadKey));
     }
 
     @Test
--- a/common/src/test/java/com/redhat/thermostat/common/dao/HostInfoDAOTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/test/java/com/redhat/thermostat/common/dao/HostInfoDAOTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -43,12 +43,16 @@
 import java.util.Collection;
 
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.times;
 
 import com.redhat.thermostat.common.model.HostInfo;
+import com.redhat.thermostat.common.storage.AgentInformation;
 import com.redhat.thermostat.common.storage.Category;
 import com.redhat.thermostat.common.storage.Chunk;
 import com.redhat.thermostat.common.storage.Cursor;
@@ -57,6 +61,13 @@
 
 public class HostInfoDAOTest {
 
+    private static final String HOST_NAME = "a host name";
+    private static final String OS_NAME = "some os";
+    private static final String OS_KERNEL = "some kernel";
+    private static final String CPU_MODEL = "some cpu that runs fast";
+    private static final int CPU_NUM = -1;
+    private static final long MEMORY_TOTAL = 0xCAFEBABEl;
+
     @Test
     public void testCategory() {
         assertEquals("host-info", HostInfoDAO.hostInfoCategory.getName());
@@ -73,12 +84,6 @@
 
     @Test
     public void testGetHostInfo() {
-        final String HOST_NAME = "a host name";
-        final String OS_NAME = "some os";
-        final String OS_KERNEL = "some kernel";
-        final String CPU_MODEL = "some cpu that runs fast";
-        final int CPU_NUM = -1;
-        final long MEMORY_TOTAL = 0xCAFEBABEl;
 
         Chunk chunk = new Chunk(HostInfoDAO.hostInfoCategory, false);
         chunk.put(HostInfoDAO.hostNameKey, HOST_NAME);
@@ -125,7 +130,8 @@
 
         Storage storage = mock(Storage.class);
         when(storage.findAllFromCategory(HostInfoDAO.hostInfoCategory)).thenReturn(cursor);
-
+        when(storage.findAll(any(Chunk.class))).thenReturn(cursor);
+        
         return storage;
     }
 
@@ -161,8 +167,29 @@
 
         Storage storage = mock(Storage.class);
         when(storage.findAllFromCategory(HostInfoDAO.hostInfoCategory)).thenReturn(cursor);
+        when(storage.findAll(any(Chunk.class))).thenReturn(cursor);
+        
+        return storage;
+    }
 
-        return storage;
+    @Test
+    public void testPutHostInfo() {
+        Storage storage = mock(Storage.class);
+        HostInfo info = new HostInfo(HOST_NAME, OS_NAME, OS_KERNEL, CPU_MODEL, CPU_NUM, MEMORY_TOTAL);
+        HostInfoDAO dao = new HostInfoDAOImpl(storage);
+        dao.putHostInfo(info);
+
+        ArgumentCaptor<Chunk> arg = ArgumentCaptor.forClass(Chunk.class);
+        verify(storage).putChunk(arg.capture());
+        Chunk chunk = arg.getValue();
+
+        assertEquals(HostInfoDAO.hostInfoCategory, chunk.getCategory());
+        assertEquals(HOST_NAME, chunk.get(HostInfoDAO.hostNameKey));
+        assertEquals(OS_NAME, chunk.get(HostInfoDAO.osNameKey));
+        assertEquals(OS_KERNEL, chunk.get(HostInfoDAO.osKernelKey));
+        assertEquals(CPU_MODEL, chunk.get(HostInfoDAO.cpuModelKey));
+        assertEquals((Integer) CPU_NUM, chunk.get(HostInfoDAO.cpuCountKey));
+        assertEquals((Long) MEMORY_TOTAL, chunk.get(HostInfoDAO.hostMemoryTotalKey));
     }
 
     @Test
@@ -173,4 +200,127 @@
         Long count = dao.getCount();
         assertEquals((Long) 5L, count);
     }
+    
+    @Test
+    public void getAliveHostSingle() {
+        Storage storage = setupStorageForSingleAliveHost();
+
+        HostInfoDAO hostsDAO = new HostInfoDAOImpl(storage);
+        Collection<HostRef> hosts = hostsDAO.getAliveHosts();
+
+        // cursor 3 from the above storage should not be used
+        assertEquals(1, hosts.size());
+        assertTrue(hosts.contains(new HostRef("123", "fluffhost1")));
+        verify(storage, times(2)).findAll(any(Chunk.class));
+    }
+    
+    private Storage setupStorageForSingleAliveHost() {
+        
+        // agents
+        
+        Chunk agentConfig1 = new Chunk(AgentInformation.AGENT_INFO_CATEGORY, false);
+        agentConfig1.put(Key.AGENT_ID, "123");
+        agentConfig1.put(AgentInformation.AGENT_ALIVE_KEY, true);
+        
+        Cursor cursor1 = mock(Cursor.class);
+        when(cursor1.hasNext()).thenReturn(true).thenReturn(false);
+        when(cursor1.next()).thenReturn(agentConfig1);
+        
+        // hosts
+        
+        Chunk hostConfig1 = new Chunk(HostInfoDAO.hostInfoCategory, false);
+        hostConfig1.put(HostInfoDAO.hostNameKey, "fluffhost1");
+        hostConfig1.put(Key.AGENT_ID, "123");
+        
+        Chunk hostConfig2 = new Chunk(HostInfoDAO.hostInfoCategory, false);
+        hostConfig2.put(HostInfoDAO.hostNameKey, "fluffhost2");
+        hostConfig2.put(Key.AGENT_ID, "456");
+        
+        Cursor cursor2 = mock(Cursor.class);
+        when(cursor2.hasNext()).thenReturn(true).thenReturn(false);
+        when(cursor2.next()).thenReturn(hostConfig1);
+
+        Cursor cursor3 = mock(Cursor.class);
+        when(cursor3.hasNext()).thenReturn(true).thenReturn(false);
+        when(cursor3.next()).thenReturn(hostConfig2);
+        
+        // storage
+        
+        Storage storage = mock(Storage.class);
+        when(storage.findAll(any(Chunk.class))).thenReturn(cursor1).thenReturn(cursor2).thenReturn(cursor3);
+        
+        return storage;
+    }
+    
+    @Test
+    public void getAliveHost3() {
+        Storage storage = setupStorageForSingleAliveHost3();
+
+        HostInfoDAO hostsDAO = new HostInfoDAOImpl(storage);
+        Collection<HostRef> hosts = hostsDAO.getAliveHosts();
+
+        // cursor 3 from the above storage should not be used
+        assertEquals(3, hosts.size());
+        assertTrue(hosts.contains(new HostRef("123", "fluffhost1")));
+        assertTrue(hosts.contains(new HostRef("456", "fluffhost2")));
+        assertTrue(hosts.contains(new HostRef("678", "fluffhost3")));
+        verify(storage, times(4)).findAll(any(Chunk.class));
+    }
+    
+    private Storage setupStorageForSingleAliveHost3() {
+        
+        // agents
+        
+        Chunk agentConfig1 = new Chunk(AgentInformation.AGENT_INFO_CATEGORY, false);
+        agentConfig1.put(Key.AGENT_ID, "123");
+        agentConfig1.put(AgentInformation.AGENT_ALIVE_KEY, true);
+        
+        Chunk agentConfig2 = new Chunk(AgentInformation.AGENT_INFO_CATEGORY, false);
+        agentConfig2.put(Key.AGENT_ID, "456");
+        agentConfig2.put(AgentInformation.AGENT_ALIVE_KEY, true);
+        
+        Chunk agentConfig3 = new Chunk(AgentInformation.AGENT_INFO_CATEGORY, false);
+        agentConfig3.put(Key.AGENT_ID, "678");
+        agentConfig3.put(AgentInformation.AGENT_ALIVE_KEY, true);
+        
+        Cursor cursor1 = mock(Cursor.class);
+        when(cursor1.hasNext()).thenReturn(true).thenReturn(true).thenReturn(true).thenReturn(false);
+        when(cursor1.next()).thenReturn(agentConfig1).thenReturn(agentConfig2).thenReturn(agentConfig3);
+        
+        // hosts
+        
+        Chunk hostConfig1 = new Chunk(HostInfoDAO.hostInfoCategory, false);
+        hostConfig1.put(HostInfoDAO.hostNameKey, "fluffhost1");
+        hostConfig1.put(Key.AGENT_ID, "123");
+        
+        Chunk hostConfig2 = new Chunk(HostInfoDAO.hostInfoCategory, false);
+        hostConfig2.put(HostInfoDAO.hostNameKey, "fluffhost2");
+        hostConfig2.put(Key.AGENT_ID, "456");
+        
+        Chunk hostConfig3 = new Chunk(HostInfoDAO.hostInfoCategory, false);
+        hostConfig3.put(HostInfoDAO.hostNameKey, "fluffhost3");
+        hostConfig3.put(Key.AGENT_ID, "678");
+        
+        Cursor cursor2 = mock(Cursor.class);
+        when(cursor2.hasNext()).thenReturn(true).thenReturn(false);
+        when(cursor2.next()).thenReturn(hostConfig1);
+
+        Cursor cursor3 = mock(Cursor.class);
+        when(cursor3.hasNext()).thenReturn(true).thenReturn(false);
+        when(cursor3.next()).thenReturn(hostConfig2);
+        
+        Cursor cursor4 = mock(Cursor.class);
+        when(cursor4.hasNext()).thenReturn(true).thenReturn(false);
+        when(cursor4.next()).thenReturn(hostConfig3);
+        
+        // storage
+        
+        Storage storage = mock(Storage.class);
+        when(storage.findAll(any(Chunk.class))).thenReturn(cursor1).
+                                                thenReturn(cursor2).
+                                                thenReturn(cursor3).
+                                                thenReturn(cursor4);
+        
+        return storage;
+    }
 }
--- a/common/src/test/java/com/redhat/thermostat/common/dao/MemoryStatDAOTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/test/java/com/redhat/thermostat/common/dao/MemoryStatDAOTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -59,6 +59,17 @@
 import com.redhat.thermostat.common.storage.Storage;
 
 public class MemoryStatDAOTest {
+
+
+    private static long TIMESTAMP = 1;
+    private static long TOTAL = 2;
+    private static long FREE = 3;
+    private static long BUFFERS = 4;
+    private static long CACHED = 5;
+    private static long SWAP_TOTAL = 6;
+    private static long SWAP_FREE = 7;
+    private static long COMMIT_LIMIT = 8;
+
     @Test
     public void testCategory() {
         assertEquals("memory-stats", MemoryStatDAO.memoryStatCategory.getName());
@@ -78,14 +89,6 @@
 
     @Test
     public void testGetLatestMemoryStats() {
-        long TIMESTAMP = 1;
-        long TOTAL = 2;
-        long FREE = 3;
-        long BUFFERS = 4;
-        long CACHED = 5;
-        long SWAP_TOTAL = 6;
-        long SWAP_FREE = 7;
-        long COMMIT_LIMIT = 8;
 
         Chunk chunk = new Chunk(MemoryStatDAO.memoryStatCategory, false);
         chunk.put(Key.TIMESTAMP, TIMESTAMP);
@@ -129,14 +132,6 @@
 
     @Test
     public void testGetLatestMemoryStatsTwice() {
-        long TIMESTAMP = 1;
-        long TOTAL = 2;
-        long FREE = 3;
-        long BUFFERS = 4;
-        long CACHED = 5;
-        long SWAP_TOTAL = 6;
-        long SWAP_FREE = 7;
-        long COMMIT_LIMIT = 8;
 
         Chunk chunk = new Chunk(MemoryStatDAO.memoryStatCategory, false);
         chunk.put(Key.TIMESTAMP, TIMESTAMP);
@@ -168,6 +163,28 @@
     }
 
     @Test
+    public void testPutHostInfo() {
+        Storage storage = mock(Storage.class);
+        MemoryStat stat = new MemoryStat(TIMESTAMP, TOTAL, FREE, BUFFERS, CACHED, SWAP_TOTAL, SWAP_FREE, COMMIT_LIMIT);
+        MemoryStatDAO dao = new MemoryStatDAOImpl(storage);
+        dao.putMemoryStat(stat);
+
+        ArgumentCaptor<Chunk> arg = ArgumentCaptor.forClass(Chunk.class);
+        verify(storage).putChunk(arg.capture());
+        Chunk chunk = arg.getValue();
+
+        assertEquals(MemoryStatDAO.memoryStatCategory, chunk.getCategory());
+        assertEquals((Long) TIMESTAMP, chunk.get(Key.TIMESTAMP));
+        assertEquals((Long) TOTAL, chunk.get(MemoryStatDAO.memoryTotalKey));
+        assertEquals((Long) FREE, chunk.get(MemoryStatDAO.memoryFreeKey));
+        assertEquals((Long) BUFFERS, chunk.get(MemoryStatDAO.memoryBuffersKey));
+        assertEquals((Long) CACHED, chunk.get(MemoryStatDAO.memoryCachedKey));
+        assertEquals((Long) SWAP_TOTAL, chunk.get(MemoryStatDAO.memorySwapTotalKey));
+        assertEquals((Long) SWAP_FREE, chunk.get(MemoryStatDAO.memorySwapFreeKey));
+        assertEquals((Long) COMMIT_LIMIT, chunk.get(MemoryStatDAO.memoryCommitLimitKey));
+    }
+
+    @Test
     public void testGetCount() {
         Storage storage = mock(Storage.class);
         when(storage.getCount(any(Category.class))).thenReturn(5L);
--- a/common/src/test/java/com/redhat/thermostat/common/dao/MongoConnectionTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,124 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.common.dao;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.net.UnknownHostException;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
-
-import com.mongodb.DB;
-import com.mongodb.DBCollection;
-import com.mongodb.Mongo;
-import com.mongodb.MongoException;
-import com.mongodb.MongoURI;
-import com.redhat.thermostat.common.config.StartupConfiguration;
-import com.redhat.thermostat.common.dao.Connection.ConnectionListener;
-import com.redhat.thermostat.common.dao.Connection.ConnectionStatus;
-import com.redhat.thermostat.common.storage.StorageConstants;
-
-@PrepareForTest(MongoConnection.class)
-@RunWith(PowerMockRunner.class)
-public class MongoConnectionTest {
-
-    private MongoConnection connection;
-    private ConnectionListener listener;
-
-    @Before
-    public void setUp() {
-        StartupConfiguration conf = mock(StartupConfiguration.class);
-        when(conf.getDBConnectionString()).thenReturn("mongodb://127.0.0.1:27518");
-        connection = new MongoConnection(conf);
-        listener = mock(ConnectionListener.class);
-        connection.addListener(listener);
-    }
-
-    @After
-    public void tearDown() {
-        connection = null;
-    }
-
-    @Test
-    public void testConnectSuccess() throws Exception {
-
-        DBCollection collection = mock(DBCollection.class);
-
-        DB db = mock(DB.class);
-        when(db.getCollection("agent-config")).thenReturn(collection);
-
-        Mongo mongo = mock(Mongo.class);
-        when(mongo.getDB(StorageConstants.THERMOSTAT_DB_NAME)).thenReturn(db);
-
-        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(mongo);
-
-        
-        connection.connect();
-
-        
-        verify(listener).changed(ConnectionStatus.CONNECTED);
-    }
-
-    @Test
-    public void testConnectUnknownHostException() throws Exception {
-
-        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenThrow(new UnknownHostException());
-
-        connection.connect();
-
-        verify(listener).changed(ConnectionStatus.FAILED_TO_CONNECT);
-    }
-
-    @Test
-    public void testConnectMongoException() throws Exception {
-
-        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenThrow(new MongoException("fluff"));
-
-        connection.connect();
-
-        verify(listener).changed(ConnectionStatus.FAILED_TO_CONNECT);
-    }
-}
--- a/common/src/test/java/com/redhat/thermostat/common/dao/MongoDAOFactoryTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/test/java/com/redhat/thermostat/common/dao/MongoDAOFactoryTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -36,39 +36,43 @@
 
 package com.redhat.thermostat.common.dao;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import org.junit.Before;
 import org.junit.Test;
 
-import com.mongodb.DB;
+import com.redhat.thermostat.common.storage.Connection;
+import com.redhat.thermostat.common.storage.Storage;
+import com.redhat.thermostat.common.storage.StorageProvider;
 
 public class MongoDAOFactoryTest {
 
-    private MongoConnection conn;
-    private ConnectionProvider connProvider;
+    private Storage storage;
+    private Connection connection;
+    private StorageProvider provider;
     private DAOFactory daoFactory;
-    private DB db;
     HostRef hostRef;
     VmRef vmRef;
 
     @Before
     public void setUp() {
-        conn = mock(MongoConnection.class);
-        connProvider = mock(ConnectionProvider.class);
-        when(connProvider.createConnection()).thenReturn(conn);
-        db = mock(DB.class);
-        when(conn.getDB()).thenReturn(db);
+        storage = mock(Storage.class);
+        connection = mock(Connection.class);
+        when(storage.getConnection()).thenReturn(connection);
+        when(connection.isConnected()).thenReturn(true);
+        provider = mock(StorageProvider.class);
+        when(provider.createStorage()).thenReturn(storage);
         hostRef = mock(HostRef.class);
         vmRef = mock(VmRef.class);
-        daoFactory = new MongoDAOFactory(connProvider);
+        daoFactory = new MongoDAOFactory(provider);
     }
 
     @Test
     public void testGetConnection() {
-        assertSame(conn, daoFactory.getConnection());
+        assertSame(storage, daoFactory.getStorage());
     }
 
     @Test
--- a/common/src/test/java/com/redhat/thermostat/common/dao/NetworkInterfaceInfoDAOTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/test/java/com/redhat/thermostat/common/dao/NetworkInterfaceInfoDAOTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -59,6 +59,10 @@
 
 public class NetworkInterfaceInfoDAOTest {
 
+    private static final String INTERFACE_NAME = "some interface. maybe eth0";
+    private static final String IPV4_ADDR = "256.256.256.256";
+    private static final String IPV6_ADDR = "100:100:100::::1";
+
     @Test
     public void testCategory() {
         Collection<Key<?>> keys;
@@ -75,9 +79,6 @@
 
     @Test
     public void testGetNetworkInterfaces() {
-        final String INTERFACE_NAME = "some interface. maybe eth0";
-        final String IPV4_ADDR = "256.256.256.256";
-        final String IPV6_ADDR = "100:100:100::::1";
 
         Chunk chunk = new Chunk(NetworkInterfaceInfoDAO.networkInfoCategory, false);
         chunk.put(NetworkInterfaceInfoDAO.ifaceKey, INTERFACE_NAME);
@@ -109,4 +110,23 @@
         assertEquals(IPV4_ADDR, info.getIp4Addr());
         assertEquals(IPV6_ADDR, info.getIp6Addr());
     }
+
+    @Test
+    public void testPutNetworkInterfaceInfo() {
+        Storage storage = mock(Storage.class);
+        NetworkInterfaceInfo info = new NetworkInterfaceInfo(INTERFACE_NAME);
+        info.setIp4Addr(IPV4_ADDR);
+        info.setIp6Addr(IPV6_ADDR);
+        NetworkInterfaceInfoDAO dao = new NetworkInterfaceInfoDAOImpl(storage);
+        dao.putNetworkInterfaceInfo(info);
+
+        ArgumentCaptor<Chunk> arg = ArgumentCaptor.forClass(Chunk.class);
+        verify(storage).putChunk(arg.capture());
+        Chunk chunk = arg.getValue();
+
+        assertEquals(NetworkInterfaceInfoDAO.networkInfoCategory, chunk.getCategory());
+        assertEquals(INTERFACE_NAME, chunk.get(NetworkInterfaceInfoDAO.ifaceKey));
+        assertEquals(IPV4_ADDR, chunk.get(NetworkInterfaceInfoDAO.ip4AddrKey));
+        assertEquals(IPV6_ADDR, chunk.get(NetworkInterfaceInfoDAO.ip6AddrKey));
+    }
 }
--- a/common/src/test/java/com/redhat/thermostat/common/dao/VmClassStatDAOTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/test/java/com/redhat/thermostat/common/dao/VmClassStatDAOTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -59,6 +59,10 @@
 
 public class VmClassStatDAOTest {
 
+    private static final Long TIMESTAMP = 1234L;
+    private static final Integer VM_ID = 123;
+    private static final Long LOADED_CLASSES = 12345L;
+
     @Test
     public void testCategory() {
         assertEquals("vm-class-stats", VmClassStatDAO.vmClassStatsCategory.getName());
@@ -74,10 +78,7 @@
     @Test
     public void testGetLatestClassStatsBasic() {
 
-        Chunk chunk = new Chunk(VmClassStatDAO.vmClassStatsCategory, false);
-        chunk.put(Key.TIMESTAMP, 1234L);
-        chunk.put(Key.VM_ID, 321);
-        chunk.put(VmClassStatDAO.loadedClassesKey, 12345L);
+        Chunk chunk = getChunk();
 
         Cursor cursor = mock(Cursor.class);
         when(cursor.hasNext()).thenReturn(true).thenReturn(false);
@@ -103,18 +104,15 @@
 
         assertEquals(1, vmClassStats.size());
         VmClassStat stat = vmClassStats.get(0);
-        assertEquals(1234L, stat.getTimeStamp());
-        assertEquals(12345L, stat.getLoadedClasses());
-        assertEquals(321, stat.getVmId());
+        assertEquals(TIMESTAMP, (Long) stat.getTimeStamp());
+        assertEquals(LOADED_CLASSES, (Long) stat.getLoadedClasses());
+        assertEquals(VM_ID, (Integer) stat.getVmId());
     }
 
     @Test
     public void testGetLatestClassStatsTwice() {
 
-        Chunk chunk = new Chunk(VmClassStatDAO.vmClassStatsCategory, false);
-        chunk.put(Key.TIMESTAMP, 1234L);
-        chunk.put(Key.VM_ID, 321);
-        chunk.put(VmClassStatDAO.loadedClassesKey, 12345L);
+        Chunk chunk = getChunk();
 
         Cursor cursor = mock(Cursor.class);
         when(cursor.hasNext()).thenReturn(true).thenReturn(false);
@@ -139,4 +137,30 @@
         verify(storage, times(2)).findAll(arg.capture());
         assertEquals("this.timestamp > 1234", arg.getValue().get(new Key<String>("$where", false)));
     }
+
+    private Chunk getChunk() {
+        Chunk chunk = new Chunk(VmClassStatDAO.vmClassStatsCategory, false);
+        chunk.put(Key.TIMESTAMP, TIMESTAMP);
+        chunk.put(Key.VM_ID, VM_ID);
+        chunk.put(VmClassStatDAO.loadedClassesKey, LOADED_CLASSES);
+        return chunk;
+    }
+
+    @Test
+    public void testPutVmClassStat() {
+
+        Storage storage = mock(Storage.class);
+        VmClassStat stat = new VmClassStat(VM_ID, TIMESTAMP, LOADED_CLASSES);
+        VmClassStatDAO dao = new VmClassStatDAOImpl(storage);
+        dao.putVmClassStat(stat);
+
+        ArgumentCaptor<Chunk> arg = ArgumentCaptor.forClass(Chunk.class);
+        verify(storage).putChunk(arg.capture());
+        Chunk chunk = arg.getValue();
+
+        assertEquals(VmClassStatDAO.vmClassStatsCategory, chunk.getCategory());
+        assertEquals((Long) TIMESTAMP, chunk.get(Key.TIMESTAMP));
+        assertEquals((Integer) VM_ID, chunk.get(Key.VM_ID));
+        assertEquals((Long) LOADED_CLASSES, chunk.get(VmClassStatDAO.loadedClassesKey));
+    }
 }
--- a/common/src/test/java/com/redhat/thermostat/common/dao/VmCpuStatDAOTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/test/java/com/redhat/thermostat/common/dao/VmCpuStatDAOTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -48,6 +48,7 @@
 import java.util.Collection;
 import java.util.List;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
@@ -59,6 +60,22 @@
 
 public class VmCpuStatDAOTest {
 
+    private static final Long TIMESTAMP = 1234L;
+    private static final String AGENT_ID = "test-agent-id";
+    private static final Integer VM_ID = 321;
+    private static final Double CPU_LOAD = 9.9;
+
+    private Chunk chunk;
+
+    @Before
+    public void setUp() {
+        chunk = new Chunk(VmCpuStatDAO.vmCpuStatCategory, false);
+        chunk.put(Key.TIMESTAMP, TIMESTAMP);
+        chunk.put(Key.AGENT_ID, AGENT_ID);
+        chunk.put(Key.VM_ID, VM_ID);
+        chunk.put(VmCpuStatDAO.vmCpuLoadKey, CPU_LOAD);
+    }
+
     @Test
     public void testCategory() {
         assertEquals("vm-cpu-stats", VmCpuStatDAO.vmCpuStatCategory.getName());
@@ -73,12 +90,6 @@
     @Test
     public void testGetLatestCpuStatsBasic() {
 
-        Chunk chunk = new Chunk(VmCpuStatDAO.vmCpuStatCategory, false);
-        chunk.put(Key.TIMESTAMP, 1234L);
-        chunk.put(Key.AGENT_ID, "test-agent-id");
-        chunk.put(Key.VM_ID, 321);
-        chunk.put(VmCpuStatDAO.vmCpuLoadKey, 9.9);
-
         Cursor cursor = mock(Cursor.class);
         when(cursor.hasNext()).thenReturn(true).thenReturn(false);
         when(cursor.next()).thenReturn(chunk);
@@ -91,7 +102,7 @@
 
         VmRef vmRef = mock(VmRef.class);
         when(vmRef.getAgent()).thenReturn(hostRef);
-        when(vmRef.getId()).thenReturn(321);
+        when(vmRef.getId()).thenReturn(VM_ID);
 
 
         VmCpuStatDAO dao = new VmCpuStatDAOImpl(storage);
@@ -103,20 +114,14 @@
 
         assertEquals(1, vmCpuStats.size());
         VmCpuStat stat = vmCpuStats.get(0);
-        assertEquals(1234L, stat.getTimeStamp());
-        assertEquals(9.9, stat.getCpuLoad(), 0.001);
-        assertEquals(321, stat.getVmId());
+        assertEquals(TIMESTAMP, (Long) stat.getTimeStamp());
+        assertEquals(CPU_LOAD, stat.getCpuLoad(), 0.001);
+        assertEquals(VM_ID, (Integer) stat.getVmId());
     }
 
     @Test
     public void testGetLatestCpuStatsTwice() {
 
-        Chunk chunk = new Chunk(VmCpuStatDAO.vmCpuStatCategory, false);
-        chunk.put(Key.AGENT_ID, "test-agent-id");
-        chunk.put(Key.TIMESTAMP, 1234L);
-        chunk.put(Key.VM_ID, 321);
-        chunk.put(VmCpuStatDAO.vmCpuLoadKey, 9.9);
-
         Cursor cursor = mock(Cursor.class);
         when(cursor.hasNext()).thenReturn(true).thenReturn(false);
         when(cursor.next()).thenReturn(chunk);
@@ -140,4 +145,20 @@
         assertEquals("this.timestamp > 1234", arg.getValue().get(new Key<String>("$where", false)));
     }
 
+    @Test
+    public void testPutVmCpuStat() {
+        Storage storage = mock(Storage.class);
+        VmCpuStat stat = new VmCpuStat(TIMESTAMP, VM_ID, CPU_LOAD);
+        VmCpuStatDAO dao = new VmCpuStatDAOImpl(storage);
+        dao.putVmCpuStat(stat);
+
+        ArgumentCaptor<Chunk> arg = ArgumentCaptor.forClass(Chunk.class);
+        verify(storage).putChunk(arg.capture());
+        Chunk chunk = arg.getValue();
+
+        assertEquals(VmCpuStatDAO.vmCpuStatCategory, chunk.getCategory());
+        assertEquals(TIMESTAMP, chunk.get(Key.TIMESTAMP));
+        assertEquals(VM_ID, chunk.get(Key.VM_ID));
+        assertEquals(CPU_LOAD, chunk.get(VmCpuStatDAO.vmCpuLoadKey));
+    }
 }
--- a/common/src/test/java/com/redhat/thermostat/common/dao/VmGcStatDAOTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/test/java/com/redhat/thermostat/common/dao/VmGcStatDAOTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -60,6 +60,12 @@
 
 public class VmGcStatDAOTest {
 
+    private static final Integer VM_ID = 123;
+    private static final Long TIMESTAMP = 456L;
+    private static final String COLLECTOR = "collector1";
+    private static final Long RUN_COUNT = 10L;
+    private static final Long WALL_TIME = 9L;
+
     @Test
     public void testCategory() {
         assertEquals("vm-gc-stats", VmGcStatDAO.vmGcStatCategory.getName());
@@ -76,12 +82,6 @@
     @Test
     public void testGetLatestVmGcStatsBasic() {
 
-        final Integer VM_ID = 123;
-        final Long TIMESTAMP = 456L;
-        final String COLLECTOR = "collector1";
-        final Long RUN_COUNT = 10L;
-        final Long WALL_TIME = 9L;
-
         Chunk chunk = new Chunk(VmGcStatDAO.vmGcStatCategory, false);
         chunk.put(Key.TIMESTAMP, TIMESTAMP);
         chunk.put(Key.VM_ID, VM_ID);
@@ -123,12 +123,6 @@
     @Test
     public void testGetLatestVmGcStatsTwice() {
 
-        final Integer VM_ID = 123;
-        final Long TIMESTAMP = 456L;
-        final String COLLECTOR = "collector1";
-        final Long RUN_COUNT = 10L;
-        final Long WALL_TIME = 9L;
-
         Chunk chunk = new Chunk(VmGcStatDAO.vmGcStatCategory, false);
         chunk.put(Key.TIMESTAMP, TIMESTAMP);
         chunk.put(Key.VM_ID, VM_ID);
@@ -158,4 +152,23 @@
         verify(storage, times(2)).findAll(arg.capture());
         assertEquals("this.timestamp > 456", arg.getValue().get(new Key<String>("$where", false)));
     }
+
+    @Test
+    public void testPutVmGcStat() {
+        Storage storage = mock(Storage.class);
+        VmGcStat stat = new VmGcStat(VM_ID, TIMESTAMP, COLLECTOR, RUN_COUNT, WALL_TIME);
+        VmGcStatDAO dao = new VmGcStatDAOImpl(storage);
+        dao.putVmGcStat(stat);
+
+        ArgumentCaptor<Chunk> arg = ArgumentCaptor.forClass(Chunk.class);
+        verify(storage).putChunk(arg.capture());
+        Chunk chunk = arg.getValue();
+
+        assertEquals(VmGcStatDAO.vmGcStatCategory, chunk.getCategory());
+        assertEquals(TIMESTAMP, chunk.get(Key.TIMESTAMP));
+        assertEquals(VM_ID, chunk.get(Key.VM_ID));
+        assertEquals(COLLECTOR, chunk.get(VmGcStatDAO.collectorKey));
+        assertEquals(RUN_COUNT, chunk.get(VmGcStatDAO.runCountKey));
+        assertEquals(WALL_TIME, chunk.get(VmGcStatDAO.wallTimeKey));
+    }
 }
--- a/common/src/test/java/com/redhat/thermostat/common/dao/VmInfoDAOTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/test/java/com/redhat/thermostat/common/dao/VmInfoDAOTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -41,6 +41,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import java.util.ArrayList;
@@ -52,6 +53,7 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 
 import com.redhat.thermostat.common.model.VmInfo;
 import com.redhat.thermostat.common.storage.Category;
@@ -237,4 +239,50 @@
         Long count = dao.getCount();
         assertEquals((Long) 5L, count);
     }
+
+    @Test
+    public void testPutVmInfo() {
+
+        Storage storage = mock(Storage.class);
+        VmInfo info = new VmInfo(vmId, startTime, stopTime, jVersion, jHome,
+                mainClass, commandLine, vmName, vmInfo, vmVersion, vmArgs,
+                props, env, libs);
+        VmInfoDAO dao = new VmInfoDAOImpl(storage);
+        dao.putVmInfo(info);
+
+        ArgumentCaptor<Chunk> arg = ArgumentCaptor.forClass(Chunk.class);
+        verify(storage).putChunk(arg.capture());
+        Chunk chunk = arg.getValue();
+
+        assertEquals(VmInfoDAO.vmInfoCategory, chunk.getCategory());
+        assertEquals((Integer) vmId, chunk.get(VmInfoDAO.vmIdKey));
+        assertEquals((Long) startTime, chunk.get(VmInfoDAO.startTimeKey));
+        assertEquals((Long) stopTime, chunk.get(VmInfoDAO.stopTimeKey));
+        assertEquals(jVersion, chunk.get(VmInfoDAO.runtimeVersionKey));
+        assertEquals(jHome, chunk.get(VmInfoDAO.javaHomeKey));
+        assertEquals(mainClass, chunk.get(VmInfoDAO.mainClassKey));
+        assertEquals(commandLine, chunk.get(VmInfoDAO.commandLineKey));
+        assertEquals(vmName, chunk.get(VmInfoDAO.vmNameKey));
+        assertEquals(vmInfo, chunk.get(VmInfoDAO.vmInfoKey));
+        assertEquals(vmVersion, chunk.get(VmInfoDAO.vmVersionKey));
+        assertEquals(vmArgs, chunk.get(VmInfoDAO.vmArgumentsKey));
+        assertEquals(props, chunk.get(VmInfoDAO.propertiesKey));
+        assertEquals(env, chunk.get(VmInfoDAO.environmentKey));
+        assertEquals(libs, chunk.get(VmInfoDAO.librariesKey));
+    }
+
+    @Test
+    public void testPutVmStoppedTime() {
+        Storage storage = mock(Storage.class);
+        VmInfoDAO dao = new VmInfoDAOImpl(storage);
+        dao.putVmStoppedTime(vmId, stopTime);
+
+        ArgumentCaptor<Chunk> arg = ArgumentCaptor.forClass(Chunk.class);
+        verify(storage).updateChunk(arg.capture());
+        Chunk chunk = arg.getValue();
+
+        assertEquals(VmInfoDAO.vmInfoCategory, chunk.getCategory());
+        assertEquals((Integer) vmId, chunk.get(VmInfoDAO.vmIdKey));
+        assertEquals((Long) stopTime, chunk.get(VmInfoDAO.stopTimeKey));
+    }
 }
--- a/common/src/test/java/com/redhat/thermostat/common/dao/VmMemoryStatDAOTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/test/java/com/redhat/thermostat/common/dao/VmMemoryStatDAOTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -43,7 +43,9 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
@@ -51,6 +53,8 @@
 import org.mockito.stubbing.Answer;
 
 import com.redhat.thermostat.common.model.VmMemoryStat;
+import com.redhat.thermostat.common.model.VmMemoryStat.Generation;
+import com.redhat.thermostat.common.model.VmMemoryStat.Space;
 import com.redhat.thermostat.common.storage.Chunk;
 import com.redhat.thermostat.common.storage.Cursor;
 import com.redhat.thermostat.common.storage.Cursor.SortDirection;
@@ -164,4 +168,75 @@
         VmMemoryStat latest = impl.getLatestMemoryStat(vmRef);
         assertTrue(latest == null);
     }
+
+    @Test
+    public void testPutVmMemoryStat() {
+
+        List<Generation> generations = new ArrayList<Generation>();
+
+        int i = 0;
+        for (String genName: new String[] { "new", "old", "perm" }) {
+            Generation gen = new Generation();
+            gen.name = genName;
+            gen.collector = gen.name;
+            generations.add(gen);
+            List<Space> spaces = new ArrayList<Space>();
+            gen.spaces = spaces;
+            String[] spaceNames = null;
+            if (genName.equals("new")) {
+                spaceNames = new String[] { "eden", "s0", "s1" };
+            } else if (genName.equals("old")) {
+                spaceNames = new String[] { "old" };
+            } else {
+                spaceNames = new String[] { "perm" };
+            }
+            for (String spaceName: spaceNames) {
+                Space space = new Space();
+                space.name = spaceName;
+                space.index = 0;
+                space.used = i++;
+                space.capacity = i++;
+                space.maxCapacity = i++;
+                spaces.add(space);
+            }
+        }
+        VmMemoryStat stat = new VmMemoryStat(1, 2, generations);
+
+        Storage storage = mock(Storage.class);
+        VmMemoryStatDAO dao = new VmMemoryStatDAOImpl(storage);
+        dao.putVmMemoryStat(stat);
+
+        ArgumentCaptor<Chunk> arg = ArgumentCaptor.forClass(Chunk.class);
+        verify(storage).putChunk(arg.capture());
+        Chunk chunk = arg.getValue();
+
+        assertEquals(VmMemoryStatDAO.vmMemoryStatsCategory, chunk.getCategory());
+        assertEquals((Long) 1l, chunk.get(new Key<Long>("timestamp", false)));
+        assertEquals((Integer) 2, chunk.get(new Key<Integer>("vm-id", false)));
+        assertEquals("new", chunk.get(new Key<String>("eden.gen", false)));
+        assertEquals("new", chunk.get(new Key<String>("eden.collector", false)));
+        assertEquals((Long) 0l, chunk.get(new Key<Long>("eden.used", false)));
+        assertEquals((Long) 1l, chunk.get(new Key<Long>("eden.capacity", false)));
+        assertEquals((Long) 2l, chunk.get(new Key<Long>("eden.max-capacity", false)));
+        assertEquals("new", chunk.get(new Key<String>("s0.gen", false)));
+        assertEquals("new", chunk.get(new Key<String>("s0.collector", false)));
+        assertEquals((Long) 3l, chunk.get(new Key<Long>("s0.used", false)));
+        assertEquals((Long) 4l, chunk.get(new Key<Long>("s0.capacity", false)));
+        assertEquals((Long) 5l, chunk.get(new Key<Long>("s0.max-capacity", false)));
+        assertEquals("new", chunk.get(new Key<String>("s1.gen", false)));
+        assertEquals("new", chunk.get(new Key<String>("s1.collector", false)));
+        assertEquals((Long) 6l, chunk.get(new Key<Long>("s1.used", false)));
+        assertEquals((Long) 7l, chunk.get(new Key<Long>("s1.capacity", false)));
+        assertEquals((Long) 8l, chunk.get(new Key<Long>("s1.max-capacity", false)));
+        assertEquals("old", chunk.get(new Key<String>("old.gen", false)));
+        assertEquals("old", chunk.get(new Key<String>("old.collector", false)));
+        assertEquals((Long) 9l, chunk.get(new Key<Long>("old.used", false)));
+        assertEquals((Long) 10l, chunk.get(new Key<Long>("old.capacity", false)));
+        assertEquals((Long) 11l, chunk.get(new Key<Long>("old.max-capacity", false)));
+        assertEquals("perm", chunk.get(new Key<String>("perm.gen", false)));
+        assertEquals("perm", chunk.get(new Key<String>("perm.collector", false)));
+        assertEquals((Long) 12l, chunk.get(new Key<Long>("perm.used", false)));
+        assertEquals((Long) 13l, chunk.get(new Key<Long>("perm.capacity", false)));
+        assertEquals((Long) 14l, chunk.get(new Key<Long>("perm.max-capacity", false)));
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/test/java/com/redhat/thermostat/common/storage/ConnectionTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common.storage;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.common.storage.Connection;
+import com.redhat.thermostat.common.storage.Connection.ConnectionStatus;
+
+public class ConnectionTest {
+
+    private Connection connection;
+
+    private Connection.ConnectionListener listener1;
+    private Connection.ConnectionListener listener2;
+
+    @Before
+    public void setUp() {
+
+        connection = new Connection() {
+            
+            @Override
+            public void disconnect() {
+                // TODO Auto-generated method stub
+                
+            }
+            
+            @Override
+            public void connect() {
+                // TODO Auto-generated method stub
+                
+            }
+        };
+        listener1 = mock(Connection.ConnectionListener.class);
+        listener2 = mock(Connection.ConnectionListener.class);
+        connection.addListener(listener1);
+        connection.addListener(listener2);
+    }
+
+    @After
+    public void tearDown() {
+        connection = null;
+        listener1 = null;
+        listener2 = null;
+    }
+
+    @Test
+    public void testListenersConnecting() throws Exception {
+        verifyListenersStatus(ConnectionStatus.CONNECTING);
+    }
+
+    @Test
+    public void testListenersConnected() throws Exception {
+        verifyListenersStatus(ConnectionStatus.CONNECTED);
+    }
+
+    @Test
+    public void testListenersFailedToConnect() throws Exception {
+        verifyListenersStatus(ConnectionStatus.FAILED_TO_CONNECT);
+    }
+
+    @Test
+    public void testListenersDisconnected() throws Exception {
+        verifyListenersStatus(ConnectionStatus.DISCONNECTED);
+    }
+
+    private void verifyListenersStatus(ConnectionStatus status) {
+        connection.fireChanged(status);
+        verify(listener1).changed(status);
+        verify(listener2).changed(status);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/test/java/com/redhat/thermostat/common/storage/MongoConnectionTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common.storage;
+
+import java.net.UnknownHostException;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import com.mongodb.DB;
+import com.mongodb.DBCollection;
+import com.mongodb.Mongo;
+import com.mongodb.MongoException;
+import com.mongodb.MongoURI;
+import com.redhat.thermostat.common.config.StartupConfiguration;
+import com.redhat.thermostat.common.storage.Connection.ConnectionListener;
+import com.redhat.thermostat.common.storage.Connection.ConnectionStatus;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@PrepareForTest(MongoConnection.class)
+@RunWith(PowerMockRunner.class)
+public class MongoConnectionTest {
+
+    private MongoConnection conn;
+    private ConnectionListener listener;
+
+    @Before
+    public void setUp() {
+        StartupConfiguration conf = mock(StartupConfiguration.class);
+        when(conf.getDBConnectionString()).thenReturn("mongodb://127.0.0.1:27518");
+        conn = new MongoConnection(conf);
+        listener = mock(ConnectionListener.class);
+        conn.addListener(listener);
+    }
+
+    @After
+    public void tearDown() {
+        conn = null;
+    }
+
+    @Test
+    public void testConnectSuccess() throws Exception {
+        DBCollection collection = mock(DBCollection.class);
+        DB db = mock(DB.class);
+        when(db.getCollection("agent-config")).thenReturn(collection);
+        Mongo m = mock(Mongo.class);
+        when(m.getDB(StorageConstants.THERMOSTAT_DB_NAME)).thenReturn(db);
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        conn.connect();
+
+        verify(listener).changed(ConnectionStatus.CONNECTED);
+    }
+
+    @Test
+    public void testConnectUnknownHostException() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenThrow(new UnknownHostException());
+        conn.connect();
+
+        verify(listener).changed(ConnectionStatus.FAILED_TO_CONNECT);
+    }
+
+    @Test
+    public void testConnectMongoException() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenThrow(new MongoException("fluff"));
+        conn.connect();
+
+        verify(listener).changed(ConnectionStatus.FAILED_TO_CONNECT);
+    }
+}
--- a/common/src/test/java/com/redhat/thermostat/common/storage/MongoStorageTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/test/java/com/redhat/thermostat/common/storage/MongoStorageTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -62,9 +62,12 @@
 import com.mongodb.DBCollection;
 import com.mongodb.DBCursor;
 import com.mongodb.DBObject;
+import com.mongodb.Mongo;
+import com.mongodb.MongoURI;
+import com.redhat.thermostat.common.config.StartupConfiguration;
 
 @RunWith(PowerMockRunner.class)
-@PrepareForTest({DBCollection.class, DB.class})
+@PrepareForTest({ DBCollection.class, DB.class, Mongo.class, MongoStorage.class, MongoConnection.class })
 public class MongoStorageTest {
 
     private static final Key<String> key1 = new Key<>("key1", false);
@@ -75,17 +78,29 @@
     private static final Category testCategory = new Category("MongoStorageTest", key1, key2, key3, key4, key5);
     private static final Category emptyTestCategory = new Category("MongoEmptyCategory");
 
+    private StartupConfiguration conf;
     private Chunk multiKeyQuery;
+    private Mongo m;
+    private DB db;
+    private DBCollection testCollection, emptyTestCollection, mockedCollection;
 
-    private MongoStorage storage;
-    private DB db;
-    private DBCollection testCollection, emptyTestCollection;
+    private MongoStorage makeStorage() throws Exception {
+        MongoStorage storage = new MongoStorage(conf);
+        storage.mapCategoryToDBCollection(testCategory, testCollection);
+        storage.mapCategoryToDBCollection(emptyTestCategory, emptyTestCollection);
+        return storage;
+    }
 
     @Before
-    public void setUp() {
-        storage = new MongoStorage(null);
+    public void setUp() throws Exception {
+        conf = mock(StartupConfiguration.class);
+        when(conf.getDBConnectionString()).thenReturn("mongodb://127.0.0.1:27518");
         db = PowerMockito.mock(DB.class);
-        storage.connect(db);
+        m = PowerMockito.mock(Mongo.class);
+        mockedCollection = mock(DBCollection.class);
+        when(m.getDB(anyString())).thenReturn(db);
+        when(db.getCollection("agent-config")).thenReturn(mockedCollection);
+        when(db.collectionExists(anyString())).thenReturn(true);
 
         BasicDBObject value1 = new BasicDBObject();
         value1.put("key1", "test1");
@@ -110,39 +125,45 @@
         when(testCollection.find()).thenReturn(cursor);
         when(testCollection.findOne(any(DBObject.class))).thenReturn(value1);
         when(testCollection.getCount()).thenReturn(2L);
-        storage.mapCategoryToDBCollection(testCategory, testCollection);
         emptyTestCollection = PowerMockito.mock(DBCollection.class);
         when(emptyTestCollection.getCount()).thenReturn(0L);
-        storage.mapCategoryToDBCollection(emptyTestCategory, emptyTestCollection);
         when(db.collectionExists(anyString())).thenReturn(false);
         when(db.createCollection(anyString(), any(DBObject.class))).thenReturn(testCollection);
     }
 
     @After
     public void tearDown() {
-        storage = null;
+        conf = null;
+        m = null;
+        db = null;
         testCollection = null;
+        emptyTestCollection = null;
         multiKeyQuery = null;
     }
 
     @Test
-    public void testCreateConnectionKey() {
-        MongoStorage mongoStorage = new MongoStorage(null);
-        mongoStorage.connect(db);
+    public void testCreateConnectionKey() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
+        storage.getConnection().connect();
         Category category = new Category("testCreateConnectionKey");
-        ConnectionKey connKey = mongoStorage.createConnectionKey(category);
+        ConnectionKey connKey = storage.createConnectionKey(category);
         assertNotNull(connKey);
     }
 
     @Test 
-    public void verifyFindAllReturnsCursor() {
+    public void verifyFindAllReturnsCursor() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
         Chunk query = new Chunk(testCategory, false);
         Cursor cursor = storage.findAll(query);
         assertNotNull(cursor);
     }
 
     @Test
-    public void verifyFindReturnsChunk() {
+    public void verifyFindReturnsChunk() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
         Chunk query = new Chunk(testCategory, false);
         query.put(key1, "test1");
         Chunk result = storage.find(query);
@@ -150,22 +171,27 @@
     }
 
     @Test
-    public void verifyFindAllCallsDBCollectionFind() {
+    public void verifyFindAllCallsDBCollectionFind() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
         Chunk query = new Chunk(testCategory, false);
         storage.findAll(query);
         verify(testCollection).find(any(DBObject.class));
     }
 
     @Test
-    public void verifyFindCallsDBCollectionFindOne() {
+    public void verifyFindCallsDBCollectionFindOne() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
         Chunk query = new Chunk(testCategory, false);
         storage.find(query);
         verify(testCollection).findOne(any(DBObject.class));
     }
 
     @Test
-    public void verifyFindAllCallsDBCollectionFindWithCorrectQuery() {
-
+    public void verifyFindAllCallsDBCollectionFindWithCorrectQuery() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
         Chunk query = new Chunk(testCategory, false);
         query.put(key1, "test");
         storage.findAll(query);
@@ -180,8 +206,9 @@
     }
 
     @Test
-    public void verifyFindCallsDBCollectionFindOneWithCorrectQuery() {
-
+    public void verifyFindCallsDBCollectionFindOneWithCorrectQuery() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
         Chunk query = new Chunk(testCategory, false);
         query.put(key1, "test");
         storage.find(query);
@@ -196,8 +223,9 @@
     }
 
     @Test
-    public void verifyFindAllWithMultiKeys() {
-
+    public void verifyFindAllWithMultiKeys() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
         storage.findAll(multiKeyQuery);
 
         ArgumentCaptor<DBObject> findArg = ArgumentCaptor.forClass(DBObject.class);
@@ -208,8 +236,9 @@
     }
 
     @Test
-    public void verifyFindWithMultiKeys() {
-
+    public void verifyFindWithMultiKeys() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
         storage.find(multiKeyQuery);
 
         ArgumentCaptor<DBObject> findArg = ArgumentCaptor.forClass(DBObject.class);
@@ -220,8 +249,9 @@
     }
 
     @Test
-    public void verifyFindReturnsCorrectChunk() {
-
+    public void verifyFindReturnsCorrectChunk() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
         // TODO find a way to test this that isn't just testing mock and converters
         Chunk query = new Chunk(testCategory, false);
         // Because we mock the DBCollection, the contents of this query don't actually determine the result.
@@ -236,8 +266,9 @@
     }
 
     @Test
-    public void verifyFindAllReturnsCorrectCursor() {
-
+    public void verifyFindAllReturnsCorrectCursor() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
         // TODO find a way to test this that isn't just testing MongoCursor
         Chunk query = new Chunk(testCategory, false);
         // Because we mock the DBCollection, the contents of this query don't actually determine the result.
@@ -249,32 +280,43 @@
     }
 
     @Test
-    public void verifyFindAllFromCategoryCallsDBCollectionFindAll() {
+    public void verifyFindAllFromCategoryCallsDBCollectionFindAll() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
         storage.findAllFromCategory(testCategory);
         verify(testCollection).find();
     }
 
     @Test
-    public void verifyFindAllFromCategoryReturnsCorrectCursor() {
+    public void verifyFindAllFromCategoryReturnsCorrectCursor() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
         Cursor cursor = storage.findAllFromCategory(testCategory);
 
         verifyDefaultCursor(cursor);
     }
 
     @Test
-    public void verifyGetCount() {
+    public void verifyGetCount() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
         long count = storage.getCount(testCategory);
         assertEquals(2, count);
     }
 
     @Test
-    public void verifyGetCountForEmptyCategory() {
+    public void verifyGetCountForEmptyCategory() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
         long count = storage.getCount(emptyTestCategory);
         assertEquals(0, count);
     }
 
     @Test
-    public void verifyGetCountForNonexistentCategory() {
+    public void verifyGetCountForNonexistentCategory() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
+        storage.getConnection().connect();
         long count = storage.getCount(new Category("NonExistent"));
         assertEquals(0, count);
     }
--- a/common/src/test/java/com/redhat/thermostat/tools/BasicCommandTest.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/common/src/test/java/com/redhat/thermostat/tools/BasicCommandTest.java	Mon Apr 16 15:41:08 2012 +0200
@@ -36,9 +36,7 @@
 
 package com.redhat.thermostat.tools;
 
-import static org.junit.Assert.*;
-
-import java.util.List;
+import static org.junit.Assert.assertNotNull;
 
 import org.junit.After;
 import org.junit.Before;
@@ -46,7 +44,6 @@
 
 import com.redhat.thermostat.cli.CommandContext;
 import com.redhat.thermostat.cli.CommandException;
-import com.redhat.thermostat.common.config.InvalidConfigurationException;
 import com.redhat.thermostat.common.config.StartupConfiguration;
 
 public class BasicCommandTest {
--- a/distribution/config/agent.properties	Mon Apr 16 14:23:46 2012 +0200
+++ b/distribution/config/agent.properties	Mon Apr 16 15:41:08 2012 +0200
@@ -3,3 +3,7 @@
 # specific backend configurations will be searched under the
 # $THERMOSTAT_HOME/backends/[backend_name.properties]
 BACKENDS=system
+
+# indicates if this agent will save its data to the database on exit
+# or rather will purge the db
+SAVE_ON_EXIT=false
--- a/tools/src/main/java/com/redhat/thermostat/tools/ThermostatService.java	Mon Apr 16 14:23:46 2012 +0200
+++ b/tools/src/main/java/com/redhat/thermostat/tools/ThermostatService.java	Mon Apr 16 15:41:08 2012 +0200
@@ -112,6 +112,16 @@
             // we are only interested in starting the agent if
             // we started the database ourselves
             case START:
+                
+                // set a shutdown hook if the db was started by us
+                Runtime.getRuntime().addShutdownHook(new Thread() {
+                    @Override
+                    public void run() {
+                        String[] args = new String[] { "storage", "--stop" };
+                        Thermostat.main(args);
+                    }
+                });
+                
                 String dbUrl = database.getConfiguration().getDBConnectionString();
                 String[] args = new String[] { "--dbUrl", dbUrl };
                 try {