changeset 117:0af8034d5ac9

Initial refactoring of Storage and Connection. Reviewed-by: vanaltj Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-March/000327.html
author Mario Torre <neugens.limasoftware@gmail.com>
date Mon, 19 Mar 2012 19:44:09 +0100
parents 83dbf9e31c6e
children 822bd53281d5 c6bbc18da9f6
files agent/src/main/java/com/redhat/thermostat/agent/Agent.java agent/src/main/java/com/redhat/thermostat/agent/Defaults.java agent/src/main/java/com/redhat/thermostat/agent/Main.java agent/src/main/java/com/redhat/thermostat/agent/config/StartupConfiguration.java agent/src/main/java/com/redhat/thermostat/backend/BackendRegistry.java agent/src/test/java/com/redhat/thermostat/agent/AgentTest.java client/src/main/java/com/redhat/thermostat/client/Main.java common/pom.xml common/src/main/java/com/redhat/thermostat/common/config/Defaults.java common/src/main/java/com/redhat/thermostat/common/config/StartupConfiguration.java common/src/main/java/com/redhat/thermostat/common/dao/Connection.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/dao/MongoDAOFactory.java common/src/main/java/com/redhat/thermostat/common/storage/ConnectionFailedException.java common/src/main/java/com/redhat/thermostat/common/storage/MongoStorage.java common/src/main/java/com/redhat/thermostat/common/storage/Storage.java common/src/test/java/com/redhat/thermostat/common/dao/ConnectionTest.java common/src/test/java/com/redhat/thermostat/common/storage/MongoStorageTest.java
diffstat 19 files changed, 607 insertions(+), 454 deletions(-) [+]
line wrap: on
line diff
--- a/agent/src/main/java/com/redhat/thermostat/agent/Agent.java	Mon Mar 19 12:39:41 2012 -0400
+++ b/agent/src/main/java/com/redhat/thermostat/agent/Agent.java	Mon Mar 19 19:44:09 2012 +0100
@@ -40,10 +40,10 @@
 import java.util.logging.Logger;
 
 import com.redhat.thermostat.agent.config.ConfigurationWatcher;
-import com.redhat.thermostat.agent.config.StartupConfiguration;
 import com.redhat.thermostat.backend.Backend;
 import com.redhat.thermostat.backend.BackendRegistry;
 import com.redhat.thermostat.common.LaunchException;
+import com.redhat.thermostat.common.config.StartupConfiguration;
 import com.redhat.thermostat.common.storage.AgentInformation;
 import com.redhat.thermostat.common.storage.BackendInformation;
 import com.redhat.thermostat.common.storage.Storage;
--- a/agent/src/main/java/com/redhat/thermostat/agent/Defaults.java	Mon Mar 19 12:39:41 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +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.agent;
-
-import java.util.logging.Level;
-
-public final class Defaults {
-
-	private Defaults() {
-	    /* Should not be instantiated */
-	}
-
-    public static final Level LOGGING_LEVEL = Level.WARNING;
-    public static final String DATABASE_URI = "mongodb://127.0.0.1";
-    public static final int MONGOS_PORT = 27517;
-    public static final int MONGOD_PORT = 27518;
-    public static final boolean LOCAL_MODE = false; // Default behaviour is to connect to cluster.
-
-}
--- a/agent/src/main/java/com/redhat/thermostat/agent/Main.java	Mon Mar 19 12:39:41 2012 -0400
+++ b/agent/src/main/java/com/redhat/thermostat/agent/Main.java	Mon Mar 19 19:44:09 2012 +0100
@@ -37,21 +37,24 @@
 package com.redhat.thermostat.agent;
 
 import java.io.IOException;
-import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import com.redhat.thermostat.agent.config.StartupConfiguration;
 import com.redhat.thermostat.backend.BackendLoadException;
 import com.redhat.thermostat.backend.BackendRegistry;
 import com.redhat.thermostat.common.Constants;
 import com.redhat.thermostat.common.LaunchException;
+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.MongoConnectionProvider;
+import com.redhat.thermostat.common.dao.Connection.ConnectionType;
+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.utils.LoggedExternalProcess;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 
 public final class Main {
@@ -84,30 +87,20 @@
 
         LoggingUtils.setGlobalLogLevel(config.getLogLevel());
 
-        String mongoScript = config.getMongoLaunchScript();
+        ConnectionProvider connProv = new MongoConnectionProvider(config);
+        Connection connection = connProv.createConnection();
         if (config.getLocalMode()) {
-            try {
-                logger.fine("Starting private mongod instance.");
-                logger.finest("Mongo launch script at: " + mongoScript);
-                int result = new LoggedExternalProcess(new String[] { mongoScript, "start" }).runAndReturnResult();
-                if (result != 0) {
-                    logger.severe("Error starting local mongod instance.");
-                    System.exit(Constants.EXIT_UNABLE_TO_CONNECT_TO_DATABASE);
-                }
-            } catch (IOException e) {
-                logger.log(Level.SEVERE, "Unable to execute script to start local mongod instance.", e);
-                System.exit(Constants.EXIT_UNABLE_TO_CONNECT_TO_DATABASE);
-            } catch (InterruptedException e) {
-                logger.log(Level.SEVERE, "Interrupted while starting local mongod instance.", e);
-                System.exit(Constants.EXIT_UNABLE_TO_CONNECT_TO_DATABASE);
-            }
+            connection.setType(ConnectionType.LOCAL);
+        } else {
+            connection.setType(ConnectionType.REMOTE);
         }
-        Storage storage = new MongoStorage();
+        
+        Storage storage = new MongoStorage(connection);
         try {
-            storage.connect(config.getDatabaseURIAsString());
+            storage.connect();
             logger.fine("Storage configured with database URI.");
-        } catch (UnknownHostException uhe) {
-            logger.log(Level.SEVERE, "Could not initialize storage layer.", uhe);
+        } catch (ConnectionFailedException ex) {
+            logger.log(Level.SEVERE, "Could not initialize storage layer.", ex);
             System.exit(Constants.EXIT_UNABLE_TO_CONNECT_TO_DATABASE);
         }
 
@@ -140,18 +133,5 @@
 
         agent.stop();
         logger.fine("Agent stopped.");
-        if (config.getLocalMode()) {
-            logger.fine("Stopping private mongod instance.");
-            try {
-                int result = new LoggedExternalProcess(new String[] { mongoScript, "stop" }).runAndReturnResult();
-                if (result != 0) {
-                    logger.severe("Error stopping local mongod instance.");
-                }
-            } catch (IOException e) {
-                logger.log(Level.WARNING, "Unable to execute script to stop private mongod instance.", e);
-            } catch (InterruptedException e) {
-                logger.log(Level.SEVERE, "Interrupted while stopping local mongod instance.", e);
-            }
-        }
     }
 }
--- a/agent/src/main/java/com/redhat/thermostat/agent/config/StartupConfiguration.java	Mon Mar 19 12:39:41 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,269 +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.agent.config;
-
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.Reader;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Properties;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.agent.Defaults;
-import com.redhat.thermostat.common.Constants;
-import com.redhat.thermostat.common.LaunchException;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-public class StartupConfiguration {
-
-    private static Logger logger = LoggingUtils.getLogger(StartupConfiguration.class);
-
-    private final long startTimestamp;
-
-    private Properties props = new Properties();
-    private Level logLevel;
-    private boolean localMode;
-    private int mongodPort;
-    private String mongoScript;
-    private int mongosPort;
-    private String databaseURI;
-    private String completeDatabaseURI;
-    private Arguments arguments;
-
-    private String hostname;
-
-    public StartupConfiguration(long startTime, String[] args) throws LaunchException {
-
-        initFromDefaults();
-        initFromArguments(args);
-        initFromProperties();
-
-        if (localMode) {
-            completeDatabaseURI = databaseURI + ":" + mongodPort;
-            hostname = Constants.AGENT_LOCAL_HOSTNAME;
-        } else {
-            completeDatabaseURI = databaseURI + ":" + mongosPort;
-            try {
-                InetAddress addr = InetAddress.getLocalHost();
-                hostname = addr.getCanonicalHostName();
-            } catch (UnknownHostException e) {
-                logger.log(Level.FINE, "Error determining local hostname.");
-            }
-        }
-        startTimestamp = startTime;
-    }
-
-    private void initFromDefaults() {
-        logLevel = Defaults.LOGGING_LEVEL;
-        localMode = Defaults.LOCAL_MODE;
-        mongodPort = Defaults.MONGOD_PORT;
-        mongosPort = Defaults.MONGOS_PORT;
-        databaseURI = Defaults.DATABASE_URI;
-    }
-
-    private void initFromProperties() throws LaunchException {
-        if (props.getProperty(Constants.AGENT_PROPERTY_MONGOD_PORT) != null) {
-            mongodPort = Integer.valueOf(props.getProperty(Constants.AGENT_PROPERTY_MONGOD_PORT));
-        }
-        if (props.getProperty(Constants.AGENT_PROPERTY_MONGO_LAUNCH_SCRIPT) != null) {
-            mongoScript = props.getProperty(Constants.AGENT_PROPERTY_MONGO_LAUNCH_SCRIPT);
-            logger.finest("Mongo launch script at: " + mongoScript);
-        } else {
-            throw new LaunchException("No mongo launch script in properties.");
-        }
-        if (props.getProperty(Constants.AGENT_PROPERTY_MONGOS_PORT) != null) {
-            mongosPort = Integer.valueOf(props.getProperty(Constants.AGENT_PROPERTY_MONGOS_PORT));
-        }
-    }
-
-    private void initFromArguments(String[] args) throws LaunchException {
-        arguments = new Arguments(args);
-        if (arguments.isModeSpecified()) {
-            localMode = arguments.getLocalMode();
-        }
-        if (arguments.isLogLevelSpecified()) {
-            logLevel = arguments.getLogLevel();
-        }
-    }
-
-    public Level getLogLevel() {
-        return logLevel;
-    }
-
-    public String getMongoLaunchScript() {
-        return mongoScript;
-    }
-
-    public String getDatabaseURIAsString() {
-        return completeDatabaseURI;
-    }
-
-    public String getHostname() {
-        return hostname;
-    }
-
-    public boolean getLocalMode() {
-        boolean local = false;
-        try {
-            local = arguments.getLocalMode();
-        } catch (IllegalStateException ise) {
-            local = false;
-        }
-        return local;
-    }
-
-    public List<String> getStartupBackendClassNames() {
-        String fullPropertyString = props.getProperty(Constants.AGENT_PROPERTY_BACKENDS);
-        if ((fullPropertyString == null) // Avoid NPE
-                || (fullPropertyString.length() == 0)) { /* If we do the split() on this empty string,
-                                                          * it will produce an array of size 1 containing
-                                                          * the empty string, which we do not want.
-                                                          */
-            return new ArrayList<String>();
-        } else {
-            return Arrays.asList(fullPropertyString.trim().split(","));
-        }
-    }
-
-    public Map<String, String> getStartupBackendConfigMap(String backendName) {
-        String prefix = backendName + ".";
-        Map<String, String> configMap = new HashMap<String, String>();
-        for (Entry<Object, Object> e : props.entrySet()) {
-            String key = (String) e.getKey();
-            if (key.startsWith(prefix)) {
-                String value = (String) e.getValue();
-                String mapKey = key.substring(prefix.length());
-                configMap.put(mapKey, value);
-            }
-        }
-        return configMap;
-    }
-
-    public long getStartTime() {
-        return startTimestamp;
-    }
-
-    /**
-     * Exposes the command line arguments in a more object-oriented style.
-     * <p>
-     * Please check that an option was specified (using the various is*Specified() methods) before using its value.
-     */
-    private class Arguments {
-        private final boolean localMode;
-        private final boolean modeSpecified;
-        private final Level logLevel;
-        private final boolean logLevelSpecified;
-
-        public Arguments(String[] args) throws LaunchException {
-            boolean local = false;
-            boolean explicitMode = false;
-            Level level = null;
-            boolean explicitLogLevel = false;
-            boolean noProps = true;
-            for (int index = 0; index < args.length; index++) {
-                if (args[index].equals(Constants.AGENT_ARGUMENT_LOGLEVEL)) {
-                    index++;
-                    if (index < args.length) {
-                        try {
-                            level = Level.parse(args[index].toUpperCase());
-                            explicitLogLevel = true;
-                        } catch (IllegalArgumentException iae) {
-                            throw new LaunchException("Invalid argument for " + Constants.AGENT_ARGUMENT_LOGLEVEL, iae);
-                        }
-                    } else {
-                        throw new LaunchException("Missing argument for " + Constants.AGENT_ARGUMENT_LOGLEVEL);
-                    }
-                } else if (args[index].equals(Constants.AGENT_ARGUMENT_LOCAL)) {
-                    explicitMode = true;
-                    local = true;
-                } else if (args[index].equals(Constants.AGENT_ARGUMENT_PROPERTIES)) {
-                    logger.finest("Properties argument specified.");
-                    index++;
-                    if (index < args.length) {
-                        String propFile = args[index];
-                        logger.finest("Properties file: " + propFile);
-                        try (Reader reader = new FileReader(propFile)) {
-                            props.load(reader);
-                        } catch (IOException ioe) {
-                            throw new LaunchException("Unable to read properties file at " + propFile);
-                        }
-                        noProps = false;
-                    } else {
-                        throw new LaunchException("Missing argument for " + Constants.AGENT_ARGUMENT_PROPERTIES);
-                    }
-                }
-            }
-            if (noProps) {
-                throw new LaunchException("Required properties file not specified.");
-            }
-            logLevel = level;
-            logLevelSpecified = explicitLogLevel;
-            localMode = local;
-            modeSpecified = explicitMode;
-        }
-
-        public boolean isModeSpecified() {
-            return modeSpecified;
-        }
-
-        public boolean getLocalMode() {
-            if (!isModeSpecified()) {
-                throw new IllegalStateException("local mode is not valid");
-            }
-            return localMode;
-        }
-
-        public boolean isLogLevelSpecified() {
-            return logLevelSpecified;
-        }
-
-        public Level getLogLevel() {
-            if (!isLogLevelSpecified()) {
-                throw new IllegalStateException("log level not explicitly specified");
-            }
-            return logLevel;
-        }
-    }
-}
--- a/agent/src/main/java/com/redhat/thermostat/backend/BackendRegistry.java	Mon Mar 19 12:39:41 2012 -0400
+++ b/agent/src/main/java/com/redhat/thermostat/backend/BackendRegistry.java	Mon Mar 19 19:44:09 2012 +0100
@@ -43,8 +43,8 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import com.redhat.thermostat.agent.config.StartupConfiguration;
 import com.redhat.thermostat.backend.system.SystemBackend;
+import com.redhat.thermostat.common.config.StartupConfiguration;
 import com.redhat.thermostat.common.storage.Storage;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 
--- a/agent/src/test/java/com/redhat/thermostat/agent/AgentTest.java	Mon Mar 19 12:39:41 2012 -0400
+++ b/agent/src/test/java/com/redhat/thermostat/agent/AgentTest.java	Mon Mar 19 19:44:09 2012 +0100
@@ -48,9 +48,9 @@
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
-import com.redhat.thermostat.agent.config.StartupConfiguration;
 import com.redhat.thermostat.backend.Backend;
 import com.redhat.thermostat.backend.BackendRegistry;
+import com.redhat.thermostat.common.config.StartupConfiguration;
 import com.redhat.thermostat.common.storage.AgentInformation;
 import com.redhat.thermostat.common.storage.BackendInformation;
 import com.redhat.thermostat.common.storage.Storage;
--- a/client/src/main/java/com/redhat/thermostat/client/Main.java	Mon Mar 19 12:39:41 2012 -0400
+++ b/client/src/main/java/com/redhat/thermostat/client/Main.java	Mon Mar 19 19:44:09 2012 +0100
@@ -53,6 +53,8 @@
 import com.redhat.thermostat.client.ui.LayoutDebugHelper;
 import com.redhat.thermostat.client.ui.MainWindow;
 import com.redhat.thermostat.common.Constants;
+import com.redhat.thermostat.common.LaunchException;
+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;
@@ -78,7 +80,17 @@
             System.exit(-1);
         }
 
-        ConnectionProvider connProv = new MongoConnectionProvider(arguments.getProperties());
+        StartupConfiguration config = null;
+        try {
+            config = new StartupConfiguration(System.currentTimeMillis(), args);
+        } catch (LaunchException le) {
+            logger.log(Level.SEVERE,
+                    "Unable to instantiate startup configuration.",
+                    le);
+            System.exit(Constants.EXIT_CONFIGURATION_ERROR);
+        }
+        
+        ConnectionProvider connProv = new MongoConnectionProvider(config);
         DAOFactory daoFactory = new MongoDAOFactory(connProv);
         ApplicationContext.getInstance().setDAOFactory(daoFactory);
     }
--- a/common/pom.xml	Mon Mar 19 12:39:41 2012 -0400
+++ b/common/pom.xml	Mon Mar 19 19:44:09 2012 +0100
@@ -45,7 +45,7 @@
     <artifactId>thermostat</artifactId>
     <version>0.2-SNAPSHOT</version>
   </parent>
-
+  
   <artifactId>thermostat-common</artifactId>
   <packaging>jar</packaging>
 
@@ -91,6 +91,19 @@
       <artifactId>mongo-java-driver</artifactId>
       <version>2.7.3</version>
     </dependency>
+    <dependency>
+    	<groupId>org.powermock</groupId>
+    	<artifactId>powermock-api-easymock</artifactId>
+    	<version>1.4.11</version>
+    </dependency>
+    <dependency>
+    	<groupId>org.easymock</groupId>
+    	<artifactId>easymock</artifactId>
+    	<version>3.1</version>
+    </dependency>
   </dependencies>
 
+  <properties>
+  	<argLine>-XX:-UseSplitVerifier</argLine>
+  </properties>
 </project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/common/config/Defaults.java	Mon Mar 19 19:44:09 2012 +0100
@@ -0,0 +1,53 @@
+/*
+ * 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.config;
+
+import java.util.logging.Level;
+
+final class Defaults {
+
+	private Defaults() {
+	    /* Should not be instantiated */
+	}
+
+    public static final Level LOGGING_LEVEL = Level.WARNING;
+    public static final String DATABASE_URI = "mongodb://127.0.0.1";
+    public static final int MONGOS_PORT = 27517;
+    public static final int MONGOD_PORT = 27518;
+    public static final boolean LOCAL_MODE = false; // Default behaviour is to connect to cluster.
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/common/config/StartupConfiguration.java	Mon Mar 19 19:44:09 2012 +0100
@@ -0,0 +1,281 @@
+/*
+ * 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.config;
+
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.common.config.Defaults;
+import com.redhat.thermostat.common.Constants;
+import com.redhat.thermostat.common.LaunchException;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+
+public class StartupConfiguration {
+
+    private static Logger logger = LoggingUtils.getLogger(StartupConfiguration.class);
+
+    private final long startTimestamp;
+
+    private Properties props = new Properties();
+    private Level logLevel;
+    private boolean localMode;
+    private int mongodPort;
+    private String mongoScript;
+    private int mongosPort;
+    private String databaseURI;
+    private String completeDatabaseURI;
+    private Arguments arguments;
+
+    private String hostname;
+
+    public StartupConfiguration(long startTime, String[] args) throws LaunchException {
+
+        initFromDefaults();
+        initFromArguments(args);
+        initFromProperties();
+
+        try {
+            InetAddress addr = InetAddress.getLocalHost();
+            hostname = addr.getCanonicalHostName();
+        } catch (UnknownHostException e) {
+            logger.log(Level.FINE, "Error determining local hostname.");
+        }
+
+        startTimestamp = startTime;
+    }
+
+    private void initFromDefaults() {
+        logLevel = Defaults.LOGGING_LEVEL;
+        localMode = Defaults.LOCAL_MODE;
+        mongodPort = Defaults.MONGOD_PORT;
+        mongosPort = Defaults.MONGOS_PORT;
+        databaseURI = Defaults.DATABASE_URI;
+    }
+
+    private void initFromProperties() throws LaunchException {
+        if (props.getProperty(Constants.AGENT_PROPERTY_MONGOD_PORT) != null) {
+            mongodPort = Integer.valueOf(props.getProperty(Constants.AGENT_PROPERTY_MONGOD_PORT));
+        }
+        if (props.getProperty(Constants.AGENT_PROPERTY_MONGO_LAUNCH_SCRIPT) != null) {
+            mongoScript = props.getProperty(Constants.AGENT_PROPERTY_MONGO_LAUNCH_SCRIPT);
+            logger.finest("Mongo launch script at: " + mongoScript);
+        } else {
+            throw new LaunchException("No mongo launch script in properties.");
+        }
+        if (props.getProperty(Constants.AGENT_PROPERTY_MONGOS_PORT) != null) {
+            mongosPort = Integer.valueOf(props.getProperty(Constants.AGENT_PROPERTY_MONGOS_PORT));
+        }
+    }
+
+    private void initFromArguments(String[] args) throws LaunchException {
+        arguments = new Arguments(args);
+        if (arguments.isModeSpecified()) {
+            localMode = arguments.getLocalMode();
+        }
+        if (arguments.isLogLevelSpecified()) {
+            logLevel = arguments.getLogLevel();
+        }
+    }
+    
+    public Level getLogLevel() {
+        return logLevel;
+    }
+
+    public String getMongoLaunchScript() {
+        return mongoScript;
+    }
+
+    public synchronized String getDatabaseURIAsString() {
+        if (completeDatabaseURI == null) {
+            if (localMode) {
+                completeDatabaseURI = getDBUriString();
+            } else {
+                completeDatabaseURI = getClusterDBUriString();
+            }
+        }
+        return completeDatabaseURI;
+    }
+    
+    public String getDBUriString() {
+        return databaseURI + ":" + mongodPort;
+    }
+    
+    // FIXME: this needs to go away, it's an implementation detail
+    public String getClusterDBUriString() {
+        return databaseURI + ":" + mongosPort;
+    }    
+    
+
+    public String getHostname() {
+        return hostname;
+    }
+
+    public boolean getLocalMode() {
+        boolean local = false;
+        try {
+            local = arguments.getLocalMode();
+        } catch (IllegalStateException ise) {
+            local = false;
+        }
+        return local;
+    }
+
+    public List<String> getStartupBackendClassNames() {
+        String fullPropertyString = props.getProperty(Constants.AGENT_PROPERTY_BACKENDS);
+        if ((fullPropertyString == null) // Avoid NPE
+                || (fullPropertyString.length() == 0)) { /* If we do the split() on this empty string,
+                                                          * it will produce an array of size 1 containing
+                                                          * the empty string, which we do not want.
+                                                          */
+            return new ArrayList<String>();
+        } else {
+            return Arrays.asList(fullPropertyString.trim().split(","));
+        }
+    }
+
+    public Map<String, String> getStartupBackendConfigMap(String backendName) {
+        String prefix = backendName + ".";
+        Map<String, String> configMap = new HashMap<String, String>();
+        for (Entry<Object, Object> e : props.entrySet()) {
+            String key = (String) e.getKey();
+            if (key.startsWith(prefix)) {
+                String value = (String) e.getValue();
+                String mapKey = key.substring(prefix.length());
+                configMap.put(mapKey, value);
+            }
+        }
+        return configMap;
+    }
+
+    public long getStartTime() {
+        return startTimestamp;
+    }
+
+    /**
+     * Exposes the command line arguments in a more object-oriented style.
+     * <p>
+     * Please check that an option was specified (using the various is*Specified() methods) before using its value.
+     */
+    private class Arguments {
+        private final boolean localMode;
+        private final boolean modeSpecified;
+        private final Level logLevel;
+        private final boolean logLevelSpecified;
+
+        public Arguments(String[] args) throws LaunchException {
+            boolean local = false;
+            boolean explicitMode = false;
+            Level level = null;
+            boolean explicitLogLevel = false;
+            boolean noProps = true;
+            for (int index = 0; index < args.length; index++) {
+                if (args[index].equals(Constants.AGENT_ARGUMENT_LOGLEVEL)) {
+                    index++;
+                    if (index < args.length) {
+                        try {
+                            level = Level.parse(args[index].toUpperCase());
+                            explicitLogLevel = true;
+                        } catch (IllegalArgumentException iae) {
+                            throw new LaunchException("Invalid argument for " + Constants.AGENT_ARGUMENT_LOGLEVEL, iae);
+                        }
+                    } else {
+                        throw new LaunchException("Missing argument for " + Constants.AGENT_ARGUMENT_LOGLEVEL);
+                    }
+                } else if (args[index].equals(Constants.AGENT_ARGUMENT_LOCAL)) {
+                    explicitMode = true;
+                    local = true;
+                } else if (args[index].equals(Constants.AGENT_ARGUMENT_PROPERTIES)) {
+                    logger.finest("Properties argument specified.");
+                    index++;
+                    if (index < args.length) {
+                        String propFile = args[index];
+                        logger.finest("Properties file: " + propFile);
+                        try (Reader reader = new FileReader(propFile)) {
+                            props.load(reader);
+                        } catch (IOException ioe) {
+                            throw new LaunchException("Unable to read properties file at " + propFile);
+                        }
+                        noProps = false;
+                    } else {
+                        throw new LaunchException("Missing argument for " + Constants.AGENT_ARGUMENT_PROPERTIES);
+                    }
+                }
+            }
+            if (noProps) {
+                throw new LaunchException("Required properties file not specified.");
+            }
+            logLevel = level;
+            logLevelSpecified = explicitLogLevel;
+            localMode = local;
+            modeSpecified = explicitMode;
+        }
+
+        public boolean isModeSpecified() {
+            return modeSpecified;
+        }
+
+        public boolean getLocalMode() {
+            if (!isModeSpecified()) {
+                throw new IllegalStateException("local mode is not valid");
+            }
+            return localMode;
+        }
+
+        public boolean isLogLevelSpecified() {
+            return logLevelSpecified;
+        }
+
+        public Level getLogLevel() {
+            if (!isLogLevelSpecified()) {
+                throw new IllegalStateException("log level not explicitly specified");
+            }
+            return logLevel;
+        }
+    }
+}
--- a/common/src/main/java/com/redhat/thermostat/common/dao/Connection.java	Mon Mar 19 12:39:41 2012 -0400
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/Connection.java	Mon Mar 19 19:44:09 2012 +0100
@@ -69,6 +69,7 @@
 
     public enum ConnectionStatus {
         CONNECTED,
+        CONNECTING,
         FAILED_TO_CONNECT,
         DISCONNECTED,
     }
--- a/common/src/main/java/com/redhat/thermostat/common/dao/MongoConnection.java	Mon Mar 19 12:39:41 2012 -0400
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/MongoConnection.java	Mon Mar 19 19:44:09 2012 +0100
@@ -36,20 +36,16 @@
 
 package com.redhat.thermostat.common.dao;
 
-import java.io.IOException;
-import java.io.OutputStream;
 import java.net.UnknownHostException;
-import java.util.Properties;
 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.Constants;
 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.LoggedExternalProcess;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 
 public class MongoConnection extends Connection {
@@ -57,31 +53,24 @@
 
     private Mongo m = null;
     private DB db = null;
-    private boolean hasLocalAgent = false;
-    private Process localAgentProcess = null;
-    private Properties props;
-
-    public MongoConnection(Properties props) {
-        this.props = props;
+    
+    private StartupConfiguration conf;
+    
+    private MongoConnection() { /* nothing to do */ }
+    
+    public MongoConnection(StartupConfiguration conf) {
+        this.conf = conf;
     }
 
     @Override
     public void connect() {
         try {
-            m = new Mongo(getMongoURI());
-            db = m.getDB(StorageConstants.THERMOSTAT_DB_NAME);
+            createConnection();
             /* the mongo java driver does not ensure this connection is actually working */
-            testConnection(db);
-        } catch (MongoException e) {
-            fireChanged(ConnectionStatus.FAILED_TO_CONNECT);
-            return;
-        } catch (UnknownHostException e) {
-            fireChanged(ConnectionStatus.FAILED_TO_CONNECT);
-            return;
-        } catch (LocalAgentException e) {
-            fireChanged(ConnectionStatus.FAILED_TO_CONNECT);
-            return;
-        } catch (NotImplementedException e) {
+            testConnection();
+        } catch (MongoException | UnknownHostException |
+                 NotImplementedException | IllegalArgumentException e)
+        {
             fireChanged(ConnectionStatus.FAILED_TO_CONNECT);
             return;
         }
@@ -89,7 +78,12 @@
         connected = true;
     }
 
-    private static void testConnection(DB db) {
+    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();
     }
 
@@ -97,9 +91,7 @@
         MongoURI uri = null;
         switch (getType()) {
             case LOCAL:
-                startLocalAgent();
-                uri = new MongoURI("mongodb://127.0.0.1:"
-                        + props.getProperty(Constants.AGENT_PROPERTY_MONGOD_PORT));
+                uri = new MongoURI(conf.getDBUriString());
                 break;
             case REMOTE:
                 throw new NotImplementedException("No mongo URI implemented for REMOTE.");
@@ -109,27 +101,6 @@
         return uri;
     }
 
-    private void startLocalAgent() throws LocalAgentException {
-        int status = 0;
-        try {
-            String agentScript = props.getProperty(Constants.CLIENT_PROPERTY_AGENT_LAUNCH_SCRIPT);
-            localAgentProcess = new LoggedExternalProcess(new String[] { agentScript, "--local" }).runAndReturnProcess();
-            // Allow some time for things to get started.
-            try {
-                // TODO provide some UI feedback here instead of just seeming dead.
-                Thread.sleep(2000);
-            } catch (InterruptedException e) {
-                // ignore
-            }
-        } catch (IOException e) {
-            throw new LocalAgentException();
-        }
-        if (status != 0) {
-            throw new LocalAgentException();
-        }
-        hasLocalAgent = true;
-    }
-
     public DB getDB() {
         return db;
     }
@@ -139,29 +110,6 @@
         if (m != null) {
             m.close();
         }
-        if (hasLocalAgent) {
-            stopLocalAgent();
-        }
         connected = false;
     }
-
-    private void stopLocalAgent() {
-        // TODO this is currently using Agent's 'run until some data avail on stdin' hack.
-        // That hack will go away, at which point we will need another way to shut down.
-        OutputStream agentIn = localAgentProcess.getOutputStream();
-        byte[] anything = { 0x04 };
-        try {
-            agentIn.write(anything);
-            agentIn.flush();
-            localAgentProcess.waitFor();
-        } catch (IOException e) {
-            logger.warning("Error shutting down local agent.");
-        } catch (InterruptedException e) {
-            logger.warning("Interrupted waiting for local agent to shut down.");
-        }
-    }
-
-    private static class LocalAgentException extends RuntimeException {
-        private static final long serialVersionUID = -4507363475778127729L;
-    }
 }
--- a/common/src/main/java/com/redhat/thermostat/common/dao/MongoConnectionProvider.java	Mon Mar 19 12:39:41 2012 -0400
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/MongoConnectionProvider.java	Mon Mar 19 19:44:09 2012 +0100
@@ -36,19 +36,19 @@
 
 package com.redhat.thermostat.common.dao;
 
-import java.util.Properties;
+import com.redhat.thermostat.common.config.StartupConfiguration;
 
 public class MongoConnectionProvider implements ConnectionProvider {
 
-    private Properties connectionProperties;
+    private StartupConfiguration configuration;
 
-    public MongoConnectionProvider(Properties connProps) {
-        connectionProperties = connProps;
+    public MongoConnectionProvider(StartupConfiguration configuration) {
+        this.configuration = configuration;
     }
 
     @Override
     public Connection createConnection() {
-        return new MongoConnection(connectionProperties);
+        return new MongoConnection(configuration);
     }
 
 }
--- a/common/src/main/java/com/redhat/thermostat/common/dao/MongoDAOFactory.java	Mon Mar 19 12:39:41 2012 -0400
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/MongoDAOFactory.java	Mon Mar 19 19:44:09 2012 +0100
@@ -47,8 +47,9 @@
     private Connection connection;
 
     public MongoDAOFactory(ConnectionProvider connProv) {
-        final MongoStorage mongoStorage = new MongoStorage();
+        
         connection = connProv.createConnection();
+        final MongoStorage mongoStorage = new MongoStorage(connection);
         connection.addListener(new ConnectionListener() {
 
             @Override
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/common/storage/ConnectionFailedException.java	Mon Mar 19 19:44:09 2012 +0100
@@ -0,0 +1,41 @@
+/*
+ * 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 {
+
+}
--- a/common/src/main/java/com/redhat/thermostat/common/storage/MongoStorage.java	Mon Mar 19 12:39:41 2012 -0400
+++ b/common/src/main/java/com/redhat/thermostat/common/storage/MongoStorage.java	Mon Mar 19 19:44:09 2012 +0100
@@ -50,9 +50,9 @@
 import com.mongodb.DBCollection;
 import com.mongodb.DBCursor;
 import com.mongodb.DBObject;
-import com.mongodb.Mongo;
-import com.mongodb.MongoURI;
 import com.mongodb.WriteConcern;
+import com.redhat.thermostat.common.dao.Connection;
+import com.redhat.thermostat.common.dao.MongoConnection;
 
 /**
  * Implementation of the Storage interface that uses MongoDB to store the instrumentation data.
@@ -61,6 +61,10 @@
  */
 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";
 
@@ -70,13 +74,9 @@
     private UUID agentId = null;
 
     @Override
-    public void connect(String uri) throws UnknownHostException {
-        connect(new MongoURI(uri));
-    }
-
-    private void connect(MongoURI uri) throws UnknownHostException {
-        Mongo mongo = new Mongo(uri);
-        db = mongo.getDB(StorageConstants.THERMOSTAT_DB_NAME);
+    public void connect() throws ConnectionFailedException {
+        connection.connect();
+        db = ((MongoConnection) connection).getDB();
     }
 
     /**
--- a/common/src/main/java/com/redhat/thermostat/common/storage/Storage.java	Mon Mar 19 12:39:41 2012 -0400
+++ b/common/src/main/java/com/redhat/thermostat/common/storage/Storage.java	Mon Mar 19 19:44:09 2012 +0100
@@ -36,12 +36,19 @@
 
 package com.redhat.thermostat.common.storage;
 
-import java.net.UnknownHostException;
 import java.util.UUID;
 
+import com.redhat.thermostat.common.dao.Connection;
+
 public abstract class Storage {
 
-    public abstract void connect(String uri) throws UnknownHostException;
+    protected Connection connection;
+    
+    public Storage (Connection connection) {
+        this.connection = connection;
+    }
+    
+    public abstract void connect() throws ConnectionFailedException;
 
     public abstract void setAgentId(UUID id);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/test/java/com/redhat/thermostat/common/dao/ConnectionTest.java	Mon Mar 19 19:44:09 2012 +0100
@@ -0,0 +1,138 @@
+/*
+ * 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.concurrent.CountDownLatch;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+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.dao.Connection.ConnectionType;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.powermock.api.easymock.PowerMock.createPartialMockAndInvokeDefaultConstructor;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(MongoConnection.class)
+public class ConnectionTest {
+    
+    /**
+     * Test if the appropriate events are notified upon connection
+     * success
+     * @throws Exception 
+     */
+    @Test
+    public void testLocalConnection() throws Exception {
+        
+        // FIXME: We should not use this level of mocking, but this will be
+        // fixed when the Storage and Connection classes refactoring
+        // will be complete.
+        Connection connection =
+                createPartialMockAndInvokeDefaultConstructor(MongoConnection.class,
+                                                             "createConnection",
+                                                             "testConnection");
+        final boolean [] testPassed = new boolean[1];
+        testPassed[0] = false;
+        final CountDownLatch latch = new CountDownLatch(1);
+        
+        connection.addListener(new ConnectionListener() {
+            @Override
+            public void changed(ConnectionStatus newStatus) {
+                if (newStatus == ConnectionStatus.CONNECTING) {
+                    // only release the latch when we are done
+                    return;
+                }
+                if (newStatus == ConnectionStatus.CONNECTED) {
+                    testPassed[0] = true;
+                }
+                latch.countDown();
+            }
+        });
+        connection.connect();
+        
+        latch.await();
+        Assert.assertTrue(testPassed[0]);
+    }
+    
+    /**
+     * Test if the connection fails given an invalid db url
+     */
+    @Test
+    public void testInvalidLocalConnection() throws InterruptedException {
+        
+        StartupConfiguration conf = mock(StartupConfiguration.class);
+        when(conf.getDBUriString()).thenReturn("fluff");
+        ConnectionProvider connProv = new MongoConnectionProvider(conf);
+        
+        Connection connection = connProv.createConnection();
+        Assert.assertNotNull(connection);
+        
+        connection.setType(ConnectionType.LOCAL);
+        Assert.assertTrue(ConnectionType.LOCAL == connection.getType());
+        
+        final boolean [] testPassed = new boolean[1];
+        testPassed[0] = false;
+        final CountDownLatch latch = new CountDownLatch(1);
+        
+        connection.addListener(new ConnectionListener() {
+            @Override
+            public void changed(ConnectionStatus newStatus) {
+                if (newStatus == ConnectionStatus.CONNECTING) {
+                    // only release the latch when we are done
+                    return;
+                }
+                
+                if (newStatus == ConnectionStatus.FAILED_TO_CONNECT) {
+                    testPassed[0] = true;
+                }
+                latch.countDown();
+            }
+        });
+        connection.connect();
+        
+        latch.await();
+        Assert.assertTrue(testPassed[0]);
+    }
+}
--- a/common/src/test/java/com/redhat/thermostat/common/storage/MongoStorageTest.java	Mon Mar 19 12:39:41 2012 -0400
+++ b/common/src/test/java/com/redhat/thermostat/common/storage/MongoStorageTest.java	Mon Mar 19 19:44:09 2012 +0100
@@ -79,7 +79,7 @@
 
     @Before
     public void setUp() {
-        storage = new MongoStorage();
+        storage = new MongoStorage(null);
 
         BasicDBObject value1 = new BasicDBObject();
         value1.put("key1", "test1");
@@ -112,7 +112,7 @@
 
     @Test
     public void testCreateConnectionKey() {
-        MongoStorage mongoStorage = new MongoStorage();
+        MongoStorage mongoStorage = new MongoStorage(null);
         Category category = new Category("testCreateConnectionKey");
         ConnectionKey connKey = mongoStorage.createConnectionKey(category);
         assertNotNull(connKey);