changeset 218:6fb6e9890e67

Make agent and client connect in same way. reviewed-by: neugens review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-April/000758.html
author Jon VanAlten <jon.vanalten@redhat.com>
date Thu, 12 Apr 2012 10:37:36 -0400
parents 4e1e5e621b84
children 96b8fc1109af
files agent/src/main/java/com/redhat/thermostat/agent/AgentApplication.java client/src/main/java/com/redhat/thermostat/client/Main.java client/src/main/java/com/redhat/thermostat/client/ui/ConnectionSelectionDialog.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/DAOFactory.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/Connection.java common/src/main/java/com/redhat/thermostat/common/storage/ConnectionFailedException.java common/src/main/java/com/redhat/thermostat/common/storage/MongoConnection.java common/src/main/java/com/redhat/thermostat/common/storage/MongoStorage.java common/src/main/java/com/redhat/thermostat/common/storage/MongoStorageProvider.java common/src/main/java/com/redhat/thermostat/common/storage/Storage.java common/src/main/java/com/redhat/thermostat/common/storage/StorageProvider.java common/src/test/java/com/redhat/thermostat/common/dao/ConnectionTest.java common/src/test/java/com/redhat/thermostat/common/dao/MongoConnectionTest.java common/src/test/java/com/redhat/thermostat/common/dao/MongoDAOFactoryTest.java common/src/test/java/com/redhat/thermostat/common/storage/ConnectionTest.java common/src/test/java/com/redhat/thermostat/common/storage/MongoConnectionTest.java common/src/test/java/com/redhat/thermostat/common/storage/MongoStorageTest.java
diffstat 22 files changed, 798 insertions(+), 770 deletions(-) [+]
line wrap: on
line diff
--- a/agent/src/main/java/com/redhat/thermostat/agent/AgentApplication.java	Wed Apr 11 19:42:15 2012 +0200
+++ b/agent/src/main/java/com/redhat/thermostat/agent/AgentApplication.java	Thu Apr 12 10:37:36 2012 -0400
@@ -51,13 +51,18 @@
 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.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.Storage;
+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;
 
@@ -96,21 +101,43 @@
         }
         
         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;
+        Storage storage = daoFactory.getStorage();
         try {
             backendRegistry = new BackendRegistry(configuration, storage);
         } catch (BackendLoadException ble) {
--- a/client/src/main/java/com/redhat/thermostat/client/Main.java	Wed Apr 11 19:42:15 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/Main.java	Thu Apr 12 10:37:36 2012 -0400
@@ -56,13 +56,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 {
@@ -82,7 +82,7 @@
 
         StartupConfiguration config = new ConnectionConfiguration();
         
-        ConnectionProvider connProv = new MongoConnectionProvider(config);
+        StorageProvider connProv = new MongoStorageProvider(config);
         DAOFactory daoFactory = new MongoDAOFactory(connProv);
         ApplicationContext.getInstance().setDAOFactory(daoFactory);
         TimerFactory timerFactory = new ThreadPoolTimerFactory(1);
--- a/client/src/main/java/com/redhat/thermostat/client/ui/ConnectionSelectionDialog.java	Wed Apr 11 19:42:15 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/ConnectionSelectionDialog.java	Thu Apr 12 10:37:36 2012 -0400
@@ -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/common/src/main/java/com/redhat/thermostat/common/dao/Connection.java	Wed Apr 11 19:42:15 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	Wed Apr 11 19:42:15 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/DAOFactory.java	Wed Apr 11 19:42:15 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/DAOFactory.java	Thu Apr 12 10:37:36 2012 -0400
@@ -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/MongoConnection.java	Wed Apr 11 19:42:15 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	Wed Apr 11 19:42:15 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	Wed Apr 11 19:42:15 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/dao/MongoDAOFactory.java	Thu Apr 12 10:37:36 2012 -0400
@@ -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");
+        }
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/common/storage/Connection.java	Thu Apr 12 10:37:36 2012 -0400
@@ -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	Wed Apr 11 19:42:15 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	Thu Apr 12 10:37:36 2012 -0400
@@ -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	Wed Apr 11 19:42:15 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/storage/MongoStorage.java	Thu Apr 12 10:37:36 2012 -0400
@@ -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());
     }
@@ -346,4 +321,34 @@
         }
         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 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	Thu Apr 12 10:37:36 2012 -0400
@@ -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	Wed Apr 11 19:42:15 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/common/storage/Storage.java	Thu Apr 12 10:37:36 2012 -0400
@@ -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,6 +51,8 @@
         category.setConnectionKey(connKey);
     }
 
+    public abstract Connection getConnection();
+
     public abstract ConnectionKey createConnectionKey(Category category);
 
     public abstract void putChunk(Chunk chunk);
@@ -86,4 +70,15 @@
     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();
+
+    /**
+     * @return {@code null} if the value is invalid or missing
+     */
+    public abstract String getBackendConfig(String backendName, String configurationKey);
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/common/storage/StorageProvider.java	Thu Apr 12 10:37:36 2012 -0400
@@ -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/test/java/com/redhat/thermostat/common/dao/ConnectionTest.java	Wed Apr 11 19:42:15 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/MongoConnectionTest.java	Wed Apr 11 19:42:15 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	Wed Apr 11 19:42:15 2012 +0200
+++ b/common/src/test/java/com/redhat/thermostat/common/dao/MongoDAOFactoryTest.java	Thu Apr 12 10:37:36 2012 -0400
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/test/java/com/redhat/thermostat/common/storage/ConnectionTest.java	Thu Apr 12 10:37:36 2012 -0400
@@ -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	Thu Apr 12 10:37:36 2012 -0400
@@ -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	Wed Apr 11 19:42:15 2012 +0200
+++ b/common/src/test/java/com/redhat/thermostat/common/storage/MongoStorageTest.java	Thu Apr 12 10:37:36 2012 -0400
@@ -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);
     }