changeset 808:897b836d5b2c

Move mongo storage bindings into separate bundle. This patch moves MongoDB specific bits out of the common-core bundle. Some config classes have been moved to storage-core in order to break a dep cycle. Also, MongoConnection was catching NotImplementedException which is in common-core so can't be used in the new bundle. However, I don't see how this exception can occur in the first place so removing this unnecessary catch seemed to be the right call. Note that existing tests continue to pass. Please speak up if you think this NotImplementedException catch is necessary in MongoConnection. Mongo* classes are now all in an internal package. With this it should be fairly easy to switch to an alternative backing storage implementation as common-core is no longer mongo-java-driver dependent (at least directly). Reviewed-by: vanaltj Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-November/004292.html
author Severin Gehwolf <sgehwolf@redhat.com>
date Tue, 27 Nov 2012 14:29:35 +0100
parents fe21da90d83f
children e5cc3eac6fec
files Makefile agent/cli/src/main/java/com/redhat/thermostat/agent/cli/AgentApplication.java agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentStartupConfiguration.java client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/config/ConnectionConfiguration.java common/core/pom.xml common/core/src/main/java/com/redhat/thermostat/common/cli/AuthenticationConfiguration.java common/core/src/main/java/com/redhat/thermostat/common/internal/Activator.java common/core/src/main/java/com/redhat/thermostat/common/internal/ConnectionConfiguration.java common/core/src/main/java/com/redhat/thermostat/common/internal/DbServiceImpl.java common/core/src/main/java/com/redhat/thermostat/common/storage/MongoConnection.java common/core/src/main/java/com/redhat/thermostat/common/storage/MongoCursor.java common/core/src/main/java/com/redhat/thermostat/common/storage/MongoPojoConverter.java common/core/src/main/java/com/redhat/thermostat/common/storage/MongoQuery.java common/core/src/main/java/com/redhat/thermostat/common/storage/MongoRemove.java common/core/src/main/java/com/redhat/thermostat/common/storage/MongoStorage.java common/core/src/main/java/com/redhat/thermostat/common/storage/MongoStorageProvider.java common/core/src/main/java/com/redhat/thermostat/common/storage/MongoUpdate.java common/core/src/main/java/com/redhat/thermostat/test/MockQuery.java common/core/src/test/java/com/redhat/thermostat/common/dao/AgentInfoDAOTest.java common/core/src/test/java/com/redhat/thermostat/common/dao/BackendInfoDAOTest.java common/core/src/test/java/com/redhat/thermostat/common/dao/QueryTestHelper.java common/core/src/test/java/com/redhat/thermostat/common/dao/VmInfoDAOTest.java common/core/src/test/java/com/redhat/thermostat/common/storage/MongoConnectionTest.java common/core/src/test/java/com/redhat/thermostat/common/storage/MongoCursorTest.java common/core/src/test/java/com/redhat/thermostat/common/storage/MongoPojoConverterTest.java common/core/src/test/java/com/redhat/thermostat/common/storage/MongoQueryTest.java common/core/src/test/java/com/redhat/thermostat/common/storage/MongoStorageTest.java common/core/src/test/java/com/redhat/thermostat/common/storage/QueryTestHelper.java distribution/config/commands/agent.properties distribution/config/commands/connect.properties distribution/config/commands/dump-heap.properties distribution/config/commands/find-objects.properties distribution/config/commands/find-root.properties distribution/config/commands/gui.properties distribution/config/commands/list-heap-dumps.properties distribution/config/commands/list-vms.properties distribution/config/commands/object-info.properties distribution/config/commands/ping.properties distribution/config/commands/save-heap-dump-to-file.properties distribution/config/commands/service.properties distribution/config/commands/show-heap-histogram.properties distribution/config/commands/storage.properties distribution/config/commands/vm-info.properties distribution/config/commands/vm-stat.properties distribution/config/commands/webservice.properties eclipse/com.redhat.thermostat.client.feature/feature.xml eclipse/com.redhat.thermostat.client.feature/pom.xml eclipse/com.redhat.thermostat.eclipse/META-INF/MANIFEST.MF eclipse/com.redhat.thermostat.eclipse/pom.xml main/src/main/resources/com/redhat/thermostat/main/impl/bootstrapbundles.properties storage/core/src/main/java/com/redhat/thermostat/storage/config/AuthenticationConfiguration.java storage/core/src/main/java/com/redhat/thermostat/storage/config/ConnectionConfiguration.java storage/mongo/pom.xml storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/Activator.java storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoConnection.java storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoCursor.java storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoPojoConverter.java storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoQuery.java storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoRemove.java storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoStorage.java storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoStorageProvider.java storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoUpdate.java storage/mongo/src/main/resources/META-INF/p2.inf storage/mongo/src/test/java/com/redhat/thermostat/storage/mongodb/internal/MongoConnectionTest.java storage/mongo/src/test/java/com/redhat/thermostat/storage/mongodb/internal/MongoCursorTest.java storage/mongo/src/test/java/com/redhat/thermostat/storage/mongodb/internal/MongoPojoConverterTest.java storage/mongo/src/test/java/com/redhat/thermostat/storage/mongodb/internal/MongoQueryTest.java storage/mongo/src/test/java/com/redhat/thermostat/storage/mongodb/internal/MongoStorageTest.java storage/pom.xml web/client/src/main/java/com/redhat/thermostat/web/client/WebStorageProvider.java web/cmd/pom.xml web/cmd/src/main/java/com/redhat/thermostat/web/cmd/WebServiceLauncher.java web/common/pom.xml web/common/src/main/java/com/redhat/thermostat/web/common/StorageWrapper.java web/war/src/main/webapp/WEB-INF/web.xml
diffstat 75 files changed, 2748 insertions(+), 2561 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Wed Nov 28 09:04:08 2012 -0500
+++ b/Makefile	Tue Nov 27 14:29:35 2012 +0100
@@ -68,4 +68,4 @@
 	echo "Using private Maven repository: $(REPO_LOC)"
 
 # We only have phony targets
-.PHONY:	all core eclipse-test-deps eclipse-test-p2 eclipse create-repo-dir clean-repo echo-repo
+.PHONY:	all core core-install eclipse-test eclipse-test-p2 eclipse-test-deps jfreechart-deps jfreechart-p2 eclipse create-repo-dir clean-repo echo-repo
--- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/AgentApplication.java	Wed Nov 28 09:04:08 2012 -0500
+++ b/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/AgentApplication.java	Tue Nov 27 14:29:35 2012 +0100
@@ -64,7 +64,6 @@
 import com.redhat.thermostat.common.config.InvalidConfigurationException;
 import com.redhat.thermostat.common.dao.DAOFactory;
 import com.redhat.thermostat.common.dao.DAOFactoryImpl;
-import com.redhat.thermostat.common.storage.MongoStorageProvider;
 import com.redhat.thermostat.common.tools.BasicCommand;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.storage.core.Connection;
--- a/agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentStartupConfiguration.java	Wed Nov 28 09:04:08 2012 -0500
+++ b/agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentStartupConfiguration.java	Tue Nov 27 14:29:35 2012 +0100
@@ -36,18 +36,7 @@
 
 package com.redhat.thermostat.agent.config;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Properties;
-
-import com.redhat.thermostat.backend.BackendID;
-import com.redhat.thermostat.backend.BackendsProperties;
-import com.redhat.thermostat.common.cli.AuthenticationConfiguration;
-import com.redhat.thermostat.common.config.ConfigUtils;
-import com.redhat.thermostat.common.config.InvalidConfigurationException;
+import com.redhat.thermostat.storage.config.AuthenticationConfiguration;
 import com.redhat.thermostat.storage.config.StartupConfiguration;
 
 public class AgentStartupConfiguration implements StartupConfiguration, AuthenticationConfiguration {
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/config/ConnectionConfiguration.java	Wed Nov 28 09:04:08 2012 -0500
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/config/ConnectionConfiguration.java	Tue Nov 27 14:29:35 2012 +0100
@@ -36,8 +36,8 @@
 
 package com.redhat.thermostat.client.swing.internal.config;
 
-import com.redhat.thermostat.common.cli.AuthenticationConfiguration;
 import com.redhat.thermostat.common.config.ClientPreferences;
+import com.redhat.thermostat.storage.config.AuthenticationConfiguration;
 import com.redhat.thermostat.storage.config.StartupConfiguration;
 
 public class ConnectionConfiguration implements StartupConfiguration, AuthenticationConfiguration {
--- a/common/core/pom.xml	Wed Nov 28 09:04:08 2012 -0500
+++ b/common/core/pom.xml	Tue Nov 27 14:29:35 2012 +0100
@@ -77,7 +77,6 @@
           <instructions>
             <Bundle-SymbolicName>com.redhat.thermostat.common.core</Bundle-SymbolicName>
             <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
-            <Bundle-Activator>com.redhat.thermostat.common.internal.Activator</Bundle-Activator>
             <Export-Package>
               com.redhat.thermostat.common.cli,
               com.redhat.thermostat.common.tools,
@@ -85,8 +84,6 @@
               com.redhat.thermostat.common.appctx,
               com.redhat.thermostat.common.config,
               com.redhat.thermostat.common.dao,
-              com.redhat.thermostat.common.model,
-              com.redhat.thermostat.common.storage,
               com.redhat.thermostat.common.utils,
               com.redhat.thermostat.common.heap,
               com.redhat.thermostat.common.locale
@@ -134,22 +131,6 @@
     </dependency>
 
     <dependency>
-      <groupId>org.mongodb</groupId>
-      <artifactId>mongo-java-driver</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>commons-beanutils</groupId>
-      <artifactId>commons-beanutils</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>commons-collections</groupId>
-      <artifactId>commons-collections</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.commons</groupId>
-      <artifactId>com.springsource.org.apache.commons.logging</artifactId>
-    </dependency>
-    <dependency>
       <groupId>commons-cli</groupId>
       <artifactId>commons-cli</artifactId>
     </dependency>
--- a/common/core/src/main/java/com/redhat/thermostat/common/cli/AuthenticationConfiguration.java	Wed Nov 28 09:04:08 2012 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +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.cli;
-
-public interface AuthenticationConfiguration {
-
-    String getUsername();
-    String getPassword();
-}
--- a/common/core/src/main/java/com/redhat/thermostat/common/internal/Activator.java	Wed Nov 28 09:04:08 2012 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-package com.redhat.thermostat.common.internal;
-
-import org.osgi.framework.BundleActivator;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceRegistration;
-
-import com.redhat.thermostat.common.storage.MongoStorageProvider;
-import com.redhat.thermostat.storage.core.StorageProvider;
-
-public class Activator implements BundleActivator {
-
-    private ServiceRegistration reg;
-    
-    @Override
-    public void start(BundleContext context) throws Exception {
-        StorageProvider prov = new MongoStorageProvider();
-        reg = context.registerService(StorageProvider.class.getName(), prov, null);
-    }
-
-    @Override
-    public void stop(BundleContext context) throws Exception {
-        reg.unregister();
-    }
-
-}
--- a/common/core/src/main/java/com/redhat/thermostat/common/internal/ConnectionConfiguration.java	Wed Nov 28 09:04:08 2012 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +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.internal;
-
-import com.redhat.thermostat.common.cli.AuthenticationConfiguration;
-import com.redhat.thermostat.storage.config.StartupConfiguration;
-
-class ConnectionConfiguration implements StartupConfiguration, AuthenticationConfiguration {
-
-    private String dbUrl;
-    private String username;
-    private String password;
-
-    ConnectionConfiguration(String dbUrl, String username, String password) {
-        this.dbUrl = dbUrl;
-        this.username = username;
-        this.password = password;
-    }
-
-    @Override
-    public String getDBConnectionString() {
-        return dbUrl;
-    }
-
-    @Override
-    public String getUsername() {
-        return username;
-    }
-
-    @Override
-    public String getPassword() {
-        return password;
-    }
-}
--- a/common/core/src/main/java/com/redhat/thermostat/common/internal/DbServiceImpl.java	Wed Nov 28 09:04:08 2012 -0500
+++ b/common/core/src/main/java/com/redhat/thermostat/common/internal/DbServiceImpl.java	Tue Nov 27 14:29:35 2012 +0100
@@ -43,6 +43,7 @@
 import com.redhat.thermostat.common.DbService;
 import com.redhat.thermostat.common.dao.DAOFactory;
 import com.redhat.thermostat.common.dao.DAOFactoryImpl;
+import com.redhat.thermostat.storage.config.ConnectionConfiguration;
 import com.redhat.thermostat.storage.config.StartupConfiguration;
 import com.redhat.thermostat.storage.core.ConnectionException;
 import com.redhat.thermostat.storage.core.StorageException;
--- a/common/core/src/main/java/com/redhat/thermostat/common/storage/MongoConnection.java	Wed Nov 28 09:04:08 2012 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,122 +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;
-
-import java.io.IOException;
-import java.net.UnknownHostException;
-
-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.cli.AuthenticationConfiguration;
-import com.redhat.thermostat.storage.config.StartupConfiguration;
-import com.redhat.thermostat.storage.core.Connection;
-import com.redhat.thermostat.storage.core.ConnectionException;
-import com.redhat.thermostat.storage.core.StorageConstants;
-
-class MongoConnection extends Connection {
-
-    private Mongo m = null;
-    private DB db = null;
-    private StartupConfiguration conf;
-
-    MongoConnection(StartupConfiguration conf) {
-        this.conf = conf;
-    }
-
-    @Override
-    public void connect() {
-        try {
-            createConnection();
-            authenticateIfNecessary();
-            /* the mongo java driver does not ensure this connection is actually working */
-            testConnection();
-            connected = true;
-
-        } catch (IOException | MongoException | NotImplementedException | IllegalArgumentException e) {
-            fireChanged(ConnectionStatus.FAILED_TO_CONNECT);
-            throw new ConnectionException(e.getMessage(), e);
-        }
-        fireChanged(ConnectionStatus.CONNECTED);
-    }
-
-    private void authenticateIfNecessary() {
-        if (conf instanceof AuthenticationConfiguration) {
-            AuthenticationConfiguration authConf = (AuthenticationConfiguration) conf;
-            String username = authConf.getUsername();
-            if (username != null && ! username.equals("")) {
-                authenticate(username, authConf.getPassword());
-            }
-        }
-    }
-
-    private void authenticate(String username, String password) {
-        if (! db.authenticate(username, password.toCharArray())) {
-            throw new MongoException("Invalid username/password");
-        }
-    }
-
-    @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() {
-        String url = conf.getDBConnectionString();
-        MongoURI uri = new MongoURI(url);
-        return uri;
-    }
-
-    private void testConnection() {
-        db.getCollection("agent-config").getCount();
-    }
-}
--- a/common/core/src/main/java/com/redhat/thermostat/common/storage/MongoCursor.java	Wed Nov 28 09:04:08 2012 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +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;
-
-import com.mongodb.DBCursor;
-import com.mongodb.DBObject;
-import com.redhat.thermostat.storage.core.Cursor;
-import com.redhat.thermostat.storage.model.Pojo;
-
-class MongoCursor<T extends Pojo> implements Cursor<T> {
-
-    private DBCursor cursor;
-    private Class<T> resultClass;
-
-    MongoCursor(DBCursor cursor, Class<T> resultClass) {
-        this.cursor = cursor;
-        this.resultClass = resultClass;
-    }
-
-    @Override
-    public boolean hasNext() {
-        return cursor.hasNext();
-    }
-
-    @Override
-    public T next() {
-        DBObject next = cursor.next();
-        if (next == null) {
-            return null;
-        }
-        MongoPojoConverter converter = new MongoPojoConverter();
-        return converter.convertMongoToPojo(next, resultClass);
-    }
-
-}
--- a/common/core/src/main/java/com/redhat/thermostat/common/storage/MongoPojoConverter.java	Wed Nov 28 09:04:08 2012 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,166 +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;
-
-import java.beans.PropertyDescriptor;
-import java.lang.reflect.Array;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-import org.apache.commons.beanutils.PropertyUtils;
-
-import com.mongodb.BasicDBObject;
-import com.mongodb.DBObject;
-import com.redhat.thermostat.storage.core.Persist;
-import com.redhat.thermostat.storage.core.StorageException;
-import com.redhat.thermostat.storage.model.Pojo;
-
-class MongoPojoConverter {
-
-    public DBObject convertPojoToMongo(Pojo obj) {
-        try {
-            return convertPojoToMongoImpl(obj);
-        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException ex) {
-            throw new StorageException(ex);
-        }
-    }
-
-    private DBObject convertPojoToMongoImpl(Pojo obj) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
-        BasicDBObject dbObj = new BasicDBObject();
-        PropertyDescriptor[] descs = PropertyUtils.getPropertyDescriptors(obj);
-        for (PropertyDescriptor desc : descs) {
-            storePropertyToDBObject(obj, dbObj, desc);
-        }
-        return dbObj;
-    }
-
-    private void storePropertyToDBObject(Pojo obj, BasicDBObject dbObj, PropertyDescriptor desc) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
-        if (hasPersistentAnnotation(desc)) {
-            String name = desc.getName();
-            Object value = PropertyUtils.getProperty(obj, name);
-            if (desc.getPropertyType().isArray()) {
-                value = convertIndexedProperty(value);
-            }
-            if (value instanceof Pojo) {
-                value = convertPojoToMongoImpl((Pojo) value);
-            }
-            dbObj.put(name, value);
-        }
-    }
-
-    private Object convertIndexedProperty(Object values) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
-        int length = Array.getLength(values);
-        List list = new ArrayList(length);
-        for (int i = 0; i < length; i++) {
-            Object value = Array.get(values, i);
-            if (value instanceof Pojo) {
-                value = convertPojoToMongoImpl((Pojo) value);
-            }
-            list.add(value);
-        }
-        return list;
-    }
-
-    public <T extends Pojo> T convertMongoToPojo(DBObject dbObj, Class<T> pojoClass) {
-        try {
-            return convertMongoToPojoImpl(dbObj, pojoClass);
-        } catch (IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException ex) {
-            throw new StorageException(ex);
-        }
-    }
-
-    private <T extends Pojo> T convertMongoToPojoImpl(DBObject dbObj, Class pojoClass) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
-        if (dbObj == null) {
-            return null;
-        }
-        T pojo = (T) pojoClass.newInstance();
-        Set<String> keys = dbObj.keySet();
-        for (String name : keys) {
-            if (! name.equals("_id")) {
-                storePropertyToPojo(dbObj, pojo, name);
-            }
-        }
-        return pojo;
-    }
-
-    private <T extends Pojo> void storePropertyToPojo(DBObject dbObj, T pojo, String name)
-            throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, InstantiationException {
-
-        PropertyDescriptor desc = PropertyUtils.getPropertyDescriptor(pojo, name);
-        if (hasPersistentAnnotation(desc)) {
-            Object value = dbObj.get(name);
-            if (desc.getPropertyType().isArray()) {
-                value = convertIndexedPropertyFromMongo(desc, (List) value);
-            }
-            if (value instanceof DBObject) {
-                value = convertMongoToPojoImpl((DBObject) value, desc.getPropertyType());
-            }
-            PropertyUtils.setProperty(pojo, name, value);
-        } else {
-            throw new StorageException("no available mapping for extra property: '" + name + "' in " + pojo.getClass().getName());
-        }
-    }
-
-    private Object convertIndexedPropertyFromMongo(PropertyDescriptor desc, List values) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
-        Class componentType = desc.getPropertyType().getComponentType();
-        Object array = Array.newInstance(componentType, values.size());
-        int i = 0;
-        for (Object value : values) {
-            if (value instanceof DBObject) {
-                value = convertMongoToPojoImpl((DBObject) value, componentType);
-            }
-            Array.set(array, i, value);
-            i++;
-        }
-        return array;
-    }
-
-    private boolean hasPersistentAnnotation(PropertyDescriptor desc) {
-        if (desc == null) {
-            return false;
-        }
-        Method writeMethod = desc.getWriteMethod();
-        Method readMethod = desc.getReadMethod();
-        return writeMethod != null && writeMethod.isAnnotationPresent(Persist.class)
-               && readMethod != null && readMethod.isAnnotationPresent(Persist.class);
-    }
-
-}
--- a/common/core/src/main/java/com/redhat/thermostat/common/storage/MongoQuery.java	Wed Nov 28 09:04:08 2012 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +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;
-
-import java.util.Objects;
-
-import com.mongodb.BasicDBObject;
-import com.mongodb.DBObject;
-import com.redhat.thermostat.storage.core.AbstractQuery;
-import com.redhat.thermostat.storage.core.Category;
-import com.redhat.thermostat.storage.core.Key;
-
-public class MongoQuery extends AbstractQuery {
-
-    private BasicDBObject query = new BasicDBObject();
-    private boolean hasClauses = false;
-    private Category category;
-
-    @Override
-    public MongoQuery from(Category category) {
-        setCategory(category);
-        return this;
-    }
-
-    public Category getCategory() {
-        return category;
-    }
-
-    public void setCategory(Category category) {
-        this.category = category;
-    }
-
-    @Override
-    public <T> MongoQuery where(Key<T> key, Criteria operator, T value) {
-        return where(key.getName(), operator, value);
-    }
-
-    public MongoQuery where(String key, Criteria operator, Object value) {
-        switch (operator) {
-        case EQUALS:
-            query.put(key, value);
-            break;
-
-        case NOT_EQUAL_TO:
-            query.put(key, new BasicDBObject("$ne", value));
-            break;
-
-        case LESS_THAN:
-            query.put(key, new BasicDBObject("$lt", value));
-            break;
-
-        case LESS_THAN_OR_EQUAL_TO:
-            query.put(key, new BasicDBObject("$lte", value));
-            break;
-        case GREATER_THAN:
-            query.put(key, new BasicDBObject("$gt", value));
-            break;
-
-        case GREATER_THAN_OR_EQUAL_TO:
-            query.put(key, new BasicDBObject("$gte", value));
-            break;
-        default:
-            throw new IllegalArgumentException("MongoQuery can not handle " + operator);
-        }
-        hasClauses = true;
-        return this;
-    }
-
-    DBObject getGeneratedQuery() {
-        return query;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == this) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (!(obj instanceof MongoQuery)) {
-            return false;
-        }
-        MongoQuery other = (MongoQuery) obj;
-        return Objects.equals(getCategory(), other.getCategory()) && Objects.equals(this.query, other.query);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(getCategory(), this.query);
-    }
-
-    boolean hasClauses() {
-        return hasClauses ;
-    }
-
-}
--- a/common/core/src/main/java/com/redhat/thermostat/common/storage/MongoRemove.java	Wed Nov 28 09:04:08 2012 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +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;
-
-import com.mongodb.BasicDBObject;
-import com.mongodb.DBObject;
-import com.redhat.thermostat.storage.core.Category;
-import com.redhat.thermostat.storage.core.Key;
-import com.redhat.thermostat.storage.core.Remove;
-
-class MongoRemove implements Remove {
-
-    private Category category;
-    private DBObject query;
-
-    @Override
-    public Remove from(Category category) {
-        if (query != null) {
-            throw new IllegalStateException();
-        }
-        this.category = category;
-        return this;
-    }
-
-    Category getCategory() {
-        return category;
-    }
-
-    @Override
-    public <T> Remove where(Key<T> key, T value) {
-        if (query == null) {
-            query = new BasicDBObject();
-        }
-        query.put(key.getName(), value);
-        return this;
-    }
-
-    DBObject getQuery() {
-        return query;
-    }
-
-}
--- a/common/core/src/main/java/com/redhat/thermostat/common/storage/MongoStorage.java	Wed Nov 28 09:04:08 2012 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,315 +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;
-
-import java.io.InputStream;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-
-import com.mongodb.BasicDBObject;
-import com.mongodb.DB;
-import com.mongodb.DBCollection;
-import com.mongodb.DBCursor;
-import com.mongodb.DBObject;
-import com.mongodb.gridfs.GridFS;
-import com.mongodb.gridfs.GridFSDBFile;
-import com.mongodb.gridfs.GridFSInputFile;
-import com.redhat.thermostat.storage.config.StartupConfiguration;
-import com.redhat.thermostat.storage.core.AbstractQuery.Sort;
-import com.redhat.thermostat.storage.core.Category;
-import com.redhat.thermostat.storage.core.Connection;
-import com.redhat.thermostat.storage.core.Connection.ConnectionListener;
-import com.redhat.thermostat.storage.core.Connection.ConnectionStatus;
-import com.redhat.thermostat.storage.core.Cursor;
-import com.redhat.thermostat.storage.core.Key;
-import com.redhat.thermostat.storage.core.Query;
-import com.redhat.thermostat.storage.core.Remove;
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.core.Update;
-import com.redhat.thermostat.storage.model.AgentIdPojo;
-import com.redhat.thermostat.storage.model.Pojo;
-
-/**
- * Implementation of the Storage interface that uses MongoDB to store the instrumentation data.
- *
- * In this implementation, each CATEGORY is given a distinct collection.
- */
-public class MongoStorage implements Storage {
-
-    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;
-
-    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
-                }
-            }
-        });
-    }
-
-    @Override
-    public Connection getConnection() {
-        return conn;
-    }
-
-    @Override
-    public void setAgentId(UUID agentId) {
-        this.agentId = agentId;
-    }
-
-    @Override
-    public String getAgentId() {
-        return agentId.toString();
-    }
-
-    private String getAgentQueryKeyFromGlobalAgent() {
-        if (agentId != null) {
-            return agentId.toString();
-        } else {
-            return null;
-        }
-    }
-
-    private String getAgentQueryKeyFromChunkOrGlobalAgent(AgentIdPojo pojo) {
-        String queryKey = getAgentQueryKeyFromGlobalAgent();
-        if (queryKey != null) {
-            return queryKey;
-        } else {
-            return pojo.getAgentId();
-        }
-    }
-
-    @Override
-    public void putPojo(Category cat, boolean replace, AgentIdPojo pojo) {
-        DBCollection coll = getCachedCollection(cat);
-        MongoPojoConverter converter = new MongoPojoConverter();
-        DBObject toInsert = converter.convertPojoToMongo(pojo);
-        String agentId = getAgentQueryKeyFromChunkOrGlobalAgent(pojo);
-        toInsert.put(Key.AGENT_ID.getName(), agentId);
-        if (replace) {
-            // TODO: Split this part out into a separate method. It is a very bad practice to
-            // completely change the behaviour of a method based on a boolean flag.
-            DBObject query = new BasicDBObject();
-            Collection<Key<?>> keys = cat.getKeys();
-            for (Key<?> key : keys) {
-                if (key.isPartialCategoryKey()) {
-                    String name = key.getName();
-                    query.put(name, toInsert.get(name));
-                }
-            }
-            coll.update(query, toInsert, true, false);
-        } else {
-            coll.insert(toInsert);
-        }
-    }
-
-    @Override
-    public void updatePojo(Update update) {
-        assert update instanceof MongoUpdate;
-        MongoUpdate mongoUpdate = (MongoUpdate) update;
-        Category cat = mongoUpdate.getCategory();
-        DBCollection coll = getCachedCollection(cat);
-        DBObject query = mongoUpdate.getQuery();
-        DBObject values = mongoUpdate.getValues();
-        coll.update(query, values);
-    }
-
-    @Override
-    public void removePojo(Remove remove) {
-        assert (remove instanceof MongoRemove);
-        MongoRemove mongoRemove = (MongoRemove) remove;
-        DBObject query = mongoRemove.getQuery();
-        Category category = mongoRemove.getCategory();
-        DBCollection coll = getCachedCollection(category);
-
-        String agentId = getAgentQueryKeyFromGlobalAgent();
-        if (agentId != null) {
-            query.put(Key.AGENT_ID.getName(), agentId);
-        }
-
-        coll.remove(query);
-    }
-
-    private DBCollection getCachedCollection(Category category) {
-        String collName = category.getName();
-        DBCollection coll = collectionCache.get(collName);
-        if (coll == null && db.collectionExists(collName)) {
-            throw new IllegalStateException("Categories need to be registered before being used");
-        }
-        return coll;
-    }
-
-    // TODO: This method is only temporary to enable tests, until we come up with a better design,
-    // in particular, the collection should be stored in the category itself. It must not be called
-    // from production code.
-    void mapCategoryToDBCollection(Category category, DBCollection coll) {
-        collectionCache.put(category.getName(), coll);
-    }
-
-
-    @Override
-    public void purge() {
-        String deleteKey = getAgentQueryKeyFromGlobalAgent();
-        BasicDBObject query = new BasicDBObject(Key.AGENT_ID.getName(), deleteKey);
-        for (DBCollection coll : collectionCache.values()) {
-            coll.remove(query);
-        }
-    }
-    
-    @Override
-    public void registerCategory(Category category) {
-        String name = category.getName();
-        if (collectionCache.containsKey(name)) {
-            throw new IllegalStateException("Category may only be associated with one backend.");
-        }
-
-        DBCollection coll;
-        if (! db.collectionExists(name)) {
-            coll = db.createCollection(name, new BasicDBObject("capped", false));
-        } else {
-            coll = db.getCollection(name);
-        }
-        collectionCache.put(name, coll);
-    }
-
-    @Override
-    public Query createQuery() {
-        return new MongoQuery();
-    }
-
-    @Override
-    public Update createUpdate() {
-        return new MongoUpdate();
-    }
-
-    @Override
-    public Remove createRemove() {
-        return new MongoRemove();
-    }
-
-    @Override
-    public <T extends Pojo> Cursor<T> findAllPojos(Query query, Class<T> resultClass) {
-        MongoQuery mongoQuery =  checkAndCastQuery(query);
-        DBCollection coll = getCachedCollection(mongoQuery.getCategory());
-        DBCursor dbCursor;
-        if (mongoQuery.hasClauses()) {
-            dbCursor = coll.find(mongoQuery.getGeneratedQuery());
-        } else {
-            dbCursor = coll.find();
-        }
-        dbCursor = applySortAndLimit(mongoQuery, dbCursor);
-        return new MongoCursor<T>(dbCursor, resultClass);
-    }
-
-    private DBCursor applySortAndLimit(MongoQuery query, DBCursor dbCursor) {
-        BasicDBObject orderBy = new BasicDBObject();
-        List<Sort> sorts = query.getSorts();
-        for (Sort sort : sorts) {
-            orderBy.append(sort.getKey().getName(), sort.getDirection().getValue());
-        }
-        dbCursor.sort(orderBy);
-        int limit = query.getLimit();
-        if (limit > 0) {
-            dbCursor.limit(limit);
-        }
-        return dbCursor;
-    }
-
-
-    @Override
-    public <T extends Pojo> T findPojo(Query query, Class<T> resultClass) {
-        MongoQuery mongoQuery = checkAndCastQuery(query);
-        DBCollection coll = getCachedCollection(mongoQuery.getCategory());
-        DBObject dbResult = coll.findOne(mongoQuery.getGeneratedQuery());
-        MongoPojoConverter conv = new MongoPojoConverter();
-        return conv.convertMongoToPojo(dbResult, resultClass);
-    }
-
-    private MongoQuery checkAndCastQuery(Query query) {
-        if (!(query instanceof MongoQuery)) {
-            throw new IllegalArgumentException("MongoStorage can only handle MongoQuery");
-        }
-
-        return (MongoQuery) query;
-
-    }
-
-    @Override
-    public long getCount(Category category) {
-        DBCollection coll = getCachedCollection(category);
-        if (coll != null) {
-            return coll.getCount();
-        }
-        return 0L;
-    }
-
-    @Override
-    public void saveFile(String filename, InputStream data) {
-        GridFS gridFS = new GridFS(db);
-        GridFSInputFile inputFile = gridFS.createFile(data, filename);
-        inputFile.save();
-    }
-
-    @Override
-    public InputStream loadFile(String filename) {
-        GridFS gridFS = new GridFS(db);
-        GridFSDBFile file = gridFS.findOne(filename);
-        if (file == null) {
-            return null;
-        } else {
-            return file.getInputStream();
-        }
-    }
-
-}
--- a/common/core/src/main/java/com/redhat/thermostat/common/storage/MongoStorageProvider.java	Wed Nov 28 09:04:08 2012 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +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;
-
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-
-import com.redhat.thermostat.storage.config.StartupConfiguration;
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.core.StorageProvider;
-
-public class MongoStorageProvider implements StorageProvider {
-
-    private StartupConfiguration configuration;
-
-    public void setConfig(StartupConfiguration configuration) {
-        this.configuration = configuration;
-    }
-
-    @Override
-    public Storage createStorage() {
-        return new MongoStorage(configuration);
-    }
-
-    @Override
-    public boolean canHandleProtocol() {
-        return configuration.getDBConnectionString().startsWith("mongodb://");
-    }
-
-}
--- a/common/core/src/main/java/com/redhat/thermostat/common/storage/MongoUpdate.java	Wed Nov 28 09:04:08 2012 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +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;
-
-import com.mongodb.BasicDBObject;
-import com.mongodb.DBObject;
-import com.redhat.thermostat.storage.core.Category;
-import com.redhat.thermostat.storage.core.Key;
-import com.redhat.thermostat.storage.core.Update;
-
-// TODO: For now we utilize the Chunk based conversion, and rely on MongoStorage to
-// actually resolve the $set fields. Eventually, we want to convert to DBObject
-// directly, and take advantage of improved semantics of this class.
-class MongoUpdate implements Update {
-
-    private DBObject query;
-    private DBObject values;
-    private Category category;
-
-    @Override
-    public Update from(Category category) {
-        if (query != null || values != null) {
-            throw new IllegalStateException();
-        }
-        this.category = category;
-        return this;
-    }
-
-    Category getCategory() {
-        return category;
-    }
-
-    @Override
-    public <T> Update where(Key<T> key, T value) {
-        if (query == null) {
-            query = new BasicDBObject();
-        }
-        query.put(key.getName(), value);
-        return this;
-    }
-
-    DBObject getQuery() {
-        return query;
-    }
-
-    @Override
-    public <T> Update set(Key<T> key, T value) {
-        if (values == null) {
-            values = new BasicDBObject();
-        }
-        values.put(key.getName(), value);
-        return this;
-    }
-
-    DBObject getValues() {
-        return new BasicDBObject("$set", values);
-    }
-}
--- a/common/core/src/main/java/com/redhat/thermostat/test/MockQuery.java	Wed Nov 28 09:04:08 2012 -0500
+++ b/common/core/src/main/java/com/redhat/thermostat/test/MockQuery.java	Tue Nov 27 14:29:35 2012 +0100
@@ -40,7 +40,6 @@
 import java.util.List;
 import java.util.Objects;
 
-import com.redhat.thermostat.common.storage.MongoQuery;
 import com.redhat.thermostat.storage.core.AbstractQuery;
 import com.redhat.thermostat.storage.core.Category;
 import com.redhat.thermostat.storage.core.Key;
--- a/common/core/src/test/java/com/redhat/thermostat/common/dao/AgentInfoDAOTest.java	Wed Nov 28 09:04:08 2012 -0500
+++ b/common/core/src/test/java/com/redhat/thermostat/common/dao/AgentInfoDAOTest.java	Tue Nov 27 14:29:35 2012 +0100
@@ -52,7 +52,6 @@
 import org.junit.Before;
 import org.junit.Test;
 
-import com.redhat.thermostat.common.storage.QueryTestHelper;
 import com.redhat.thermostat.storage.core.Category;
 import com.redhat.thermostat.storage.core.Cursor;
 import com.redhat.thermostat.storage.core.Key;
--- a/common/core/src/test/java/com/redhat/thermostat/common/dao/BackendInfoDAOTest.java	Wed Nov 28 09:04:08 2012 -0500
+++ b/common/core/src/test/java/com/redhat/thermostat/common/dao/BackendInfoDAOTest.java	Tue Nov 27 14:29:35 2012 +0100
@@ -51,7 +51,6 @@
 import org.junit.Test;
 import org.mockito.InOrder;
 
-import com.redhat.thermostat.common.storage.QueryTestHelper;
 import com.redhat.thermostat.storage.core.Category;
 import com.redhat.thermostat.storage.core.Cursor;
 import com.redhat.thermostat.storage.core.Key;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/core/src/test/java/com/redhat/thermostat/common/dao/QueryTestHelper.java	Tue Nov 27 14:29:35 2012 +0100
@@ -0,0 +1,77 @@
+/*
+ * 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.when;
+
+import com.redhat.thermostat.storage.core.Category;
+import com.redhat.thermostat.storage.core.Key;
+import com.redhat.thermostat.storage.core.Query;
+import com.redhat.thermostat.storage.core.Remove;
+import com.redhat.thermostat.storage.core.Update;
+import com.redhat.thermostat.storage.core.Query.Criteria;
+
+public class QueryTestHelper {
+
+    @SuppressWarnings("unchecked")
+    public static Update createMockUpdate() {
+        Update mockUpdate = mock(Update.class);
+        when(mockUpdate.from(any(Category.class))).thenReturn(mockUpdate);
+        when(mockUpdate.where(any(Key.class), any())).thenReturn(mockUpdate);
+        when(mockUpdate.set(any(Key.class), any())).thenReturn(mockUpdate);
+        return mockUpdate;
+    }
+
+    @SuppressWarnings("unchecked")
+    public static Remove createMockRemove() {
+        Remove mockRemove = mock(Remove.class);
+        when(mockRemove.from(any(Category.class))).thenReturn(mockRemove);
+        when(mockRemove.where(any(Key.class), any())).thenReturn(mockRemove);
+        return mockRemove;
+    }
+
+    @SuppressWarnings("unchecked")
+    public static Query createMockQuery() {
+        Query mockQuery = mock(Query.class);
+        when(mockQuery.from(any(Category.class))).thenReturn(mockQuery);
+        when(mockQuery.where(any(Key.class), any(Criteria.class), any())).thenReturn(mockQuery);
+        return mockQuery;
+    }
+}
--- a/common/core/src/test/java/com/redhat/thermostat/common/dao/VmInfoDAOTest.java	Wed Nov 28 09:04:08 2012 -0500
+++ b/common/core/src/test/java/com/redhat/thermostat/common/dao/VmInfoDAOTest.java	Tue Nov 27 14:29:35 2012 +0100
@@ -53,7 +53,6 @@
 import org.junit.Before;
 import org.junit.Test;
 
-import com.redhat.thermostat.common.storage.QueryTestHelper;
 import com.redhat.thermostat.storage.core.Category;
 import com.redhat.thermostat.storage.core.Cursor;
 import com.redhat.thermostat.storage.core.Key;
--- a/common/core/src/test/java/com/redhat/thermostat/common/storage/MongoConnectionTest.java	Wed Nov 28 09:04:08 2012 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,128 +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;
-
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.io.IOException;
-
-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.storage.MongoConnection;
-import com.redhat.thermostat.storage.config.StartupConfiguration;
-import com.redhat.thermostat.storage.core.Connection;
-import com.redhat.thermostat.storage.core.ConnectionException;
-import com.redhat.thermostat.storage.core.StorageConstants;
-import com.redhat.thermostat.storage.core.Connection.ConnectionListener;
-import com.redhat.thermostat.storage.core.Connection.ConnectionStatus;
-
-@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 testConnectIOException() throws Exception {
-        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenThrow(new IOException());
-        boolean exceptionThrown = false;
-        try {
-            conn.connect();
-        } catch (ConnectionException ex) {
-            exceptionThrown = true;
-        }
-        verify(listener).changed(ConnectionStatus.FAILED_TO_CONNECT);
-        assertTrue(exceptionThrown);
-    }
-
-    @Test
-    public void testConnectMongoException() throws Exception {
-        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenThrow(new MongoException("fluff"));
-        boolean exceptionThrown = false;
-        try {
-            conn.connect();
-        } catch (ConnectionException ex) {
-            exceptionThrown = true;
-        }
-
-        verify(listener).changed(ConnectionStatus.FAILED_TO_CONNECT);
-        assertTrue(exceptionThrown);
-    }
-}
--- a/common/core/src/test/java/com/redhat/thermostat/common/storage/MongoCursorTest.java	Wed Nov 28 09:04:08 2012 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,157 +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;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.mongodb.BasicDBObject;
-import com.mongodb.DBCursor;
-import com.mongodb.DBObject;
-import com.redhat.thermostat.common.storage.MongoCursor;
-import com.redhat.thermostat.storage.core.Category;
-import com.redhat.thermostat.storage.core.Cursor;
-import com.redhat.thermostat.storage.core.Entity;
-import com.redhat.thermostat.storage.core.Key;
-import com.redhat.thermostat.storage.core.Persist;
-import com.redhat.thermostat.storage.model.BasePojo;
-
-public class MongoCursorTest {
-
-    @Entity
-    public static class TestClass extends BasePojo {
-        private String key1;
-        private String key2;
-        private String key3;
-        private String key4;
-        @Persist
-        public String getKey1() {
-            return key1;
-        }
-        @Persist
-        public void setKey1(String key1) {
-            this.key1 = key1;
-        }
-        @Persist
-        public String getKey2() {
-            return key2;
-        }
-        @Persist
-        public void setKey2(String key2) {
-            this.key2 = key2;
-        }
-        @Persist
-        public String getKey3() {
-            return key3;
-        }
-        @Persist
-        public void setKey3(String key3) {
-            this.key3 = key3;
-        }
-        @Persist
-        public String getKey4() {
-            return key4;
-        }
-        @Persist
-        public void setKey4(String key4) {
-            this.key4 = key4;
-        }
-    }
-
-    private static final Key<String> key1 = new Key<>("key1", false);
-    private static final Key<String> key2 = new Key<>("key2", false);
-    private static final Key<String> key3 = new Key<>("key3", false);
-    private static final Key<String> key4 = new Key<>("key4", false);
-
-    private static final Category testCategory = new Category("MongoCursorTest", key1, key2, key3, key4);
-
-    private DBCursor dbCursor;
-    private Cursor<TestClass> cursor;
-
-    @Before
-    public void setUp() {
-        
-        BasicDBObject value1 = new BasicDBObject();
-        value1.put("key1", "test1");
-        value1.put("key2", "test2");
-        BasicDBObject value2 = new BasicDBObject();
-        value2.put("key3", "test3");
-        value2.put("key4", "test4");
-
-        dbCursor = mock(DBCursor.class);
-        when(dbCursor.hasNext()).thenReturn(true).thenReturn(true).thenReturn(false);
-        when(dbCursor.next()).thenReturn(value1).thenReturn(value2).thenReturn(null);
-        when(dbCursor.sort(any(DBObject.class))).thenReturn(dbCursor);
-        when(dbCursor.limit(anyInt())).thenReturn(dbCursor);
-        cursor = new MongoCursor<TestClass>(dbCursor, TestClass.class);
-
-    }
-
-    @After
-    public void tearDown() {
-        dbCursor = null;
-        cursor = null;
-    }
-
-    @Test
-    public void verifySimpleCursor() {
-
-        assertTrue(cursor.hasNext());
-        TestClass obj1 = cursor.next();
-        assertEquals("test1", obj1.getKey1());
-        assertEquals("test2", obj1.getKey2());
-
-        assertTrue(cursor.hasNext());
-        TestClass obj2 = cursor.next();
-        assertEquals("test3", obj2.getKey3());
-        assertEquals("test4", obj2.getKey4());
-
-        assertFalse(cursor.hasNext());
-        assertNull(cursor.next());
-    }
-
-}
--- a/common/core/src/test/java/com/redhat/thermostat/common/storage/MongoPojoConverterTest.java	Wed Nov 28 09:04:08 2012 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,327 +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;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import java.util.Arrays;
-import java.util.List;
-
-import org.junit.Test;
-
-import com.mongodb.BasicDBObject;
-import com.mongodb.DBObject;
-import com.redhat.thermostat.common.storage.MongoPojoConverter;
-import com.redhat.thermostat.storage.core.Entity;
-import com.redhat.thermostat.storage.core.Persist;
-import com.redhat.thermostat.storage.core.StorageException;
-import com.redhat.thermostat.storage.model.Pojo;
-
-public class MongoPojoConverterTest {
-
-    @Entity
-    public static class SimplePojo implements Pojo {
-        
-        private String test;
-        private String ignored;
-
-
-        @Persist
-        public String getTest() {
-            return test;
-        }
-
-        @Persist
-        public void setTest(String test) {
-            this.test = test;
-        }
-
-        public String getIgnored() {
-            return ignored;
-        }
-
-        public void setIgnored(String ignored) {
-            this.ignored = ignored;
-        }
-        
-    }
-
-    @Entity
-    public static class NestedPojo extends SimplePojo {
-
-        private SimplePojo nested;
-
-        @Persist
-        public SimplePojo getNested() {
-            return nested;
-        }
-
-        @Persist
-        public void setNested(SimplePojo nested) {
-            this.nested = nested;
-        }
-    }
-
-    @Entity
-    public static class IndexedPojo extends SimplePojo {
-
-        private SimplePojo[] indexed;
-
-        @Persist
-        public SimplePojo[] getIndexed() {
-            return indexed;
-        }
-
-        @Persist
-        public void setIndexed(SimplePojo[] indexed) {
-            this.indexed = indexed;
-        }
-    }
-
-    @Entity
-    public static class PrimitiveIndexedPojo extends SimplePojo {
-
-        private int[] indexed;
-
-        @Persist
-        public int[] getIndexed() {
-            return indexed;
-        }
-
-        @Persist
-        public void setIndexed(int[] indexed) {
-            this.indexed = indexed;
-        }
-    }
-
-    public static class BrokenPojo1 extends SimplePojo {
-        private int broken;
-
-        @Persist
-        public void setBroken(int broken) {
-            this.broken = broken;
-        }
-    }
-
-    public static class BrokenPojo2 extends SimplePojo {
-        private int broken;
-
-        @Persist
-        public void setBroken(int broken) {
-            this.broken = broken;
-        }
-
-        public int getBroken() {
-            return broken;
-        }
-    }
-
-    @Test
-    public void testConvertSimplePojoToMongo() {
-        MongoPojoConverter conv = new MongoPojoConverter();
-        SimplePojo obj = new SimplePojo();
-        obj.setTest("fluff");
-        DBObject dbObject = conv.convertPojoToMongo(obj);
-        assertEquals(1, dbObject.keySet().size());
-        assertEquals("fluff", dbObject.get("test"));
-    }
-
-    @Test
-    public void testConvertSimplePojoFromMongo() {
-        MongoPojoConverter conv = new MongoPojoConverter();
-        DBObject dbObj = new BasicDBObject();
-        dbObj.put("test", "fluff");
-        SimplePojo obj = conv.convertMongoToPojo(dbObj, SimplePojo.class);
-        assertEquals("fluff", obj.getTest());
-        assertNull(obj.getIgnored());
-    }
-
-    @Test(expected=StorageException.class)
-    public void testConvertSimplePojoFromMongoExtraProperty() {
-        MongoPojoConverter conv = new MongoPojoConverter();
-        DBObject dbObj = new BasicDBObject();
-        dbObj.put("test", "fluff");
-        dbObj.put("foo", "bar");
-        conv.convertMongoToPojo(dbObj, SimplePojo.class);
-    }
-
-    @Test(expected=StorageException.class)
-    public void testConvertSimplePojoFromMongoBrokenPojo1() {
-        MongoPojoConverter conv = new MongoPojoConverter();
-        DBObject dbObj = new BasicDBObject();
-        dbObj.put("broken", "foo");
-        conv.convertMongoToPojo(dbObj, BrokenPojo1.class);
-    }
-
-
-    @Test(expected=StorageException.class)
-    public void testConvertSimplePojoFromMongoBrokenPojo2() {
-        MongoPojoConverter conv = new MongoPojoConverter();
-        DBObject dbObj = new BasicDBObject();
-        dbObj.put("broken", "foo");
-        conv.convertMongoToPojo(dbObj, BrokenPojo2.class);
-    }
-
-    @Test
-    public void testConvertNestedPojoToMongo() {
-        MongoPojoConverter conv = new MongoPojoConverter();
-        NestedPojo obj = new NestedPojo();
-        obj.setTest("foo");
-        SimplePojo nested = new SimplePojo();
-        nested.setTest("bar");
-        obj.setNested(nested);
-        DBObject dbObject = conv.convertPojoToMongo(obj);
-        assertEquals(2, dbObject.keySet().size());
-        assertEquals("foo", dbObject.get("test"));
-        DBObject nestedDbObj = (DBObject) dbObject.get("nested");
-        assertEquals(1, nestedDbObj.keySet().size());
-        assertEquals("bar", nestedDbObj.get("test"));
-    }
-
-    @Test
-    public void testConvertNestedPojoFromMongo() {
-        MongoPojoConverter conv = new MongoPojoConverter();
-        DBObject nested = new BasicDBObject();
-        nested.put("test", "bar");
-        DBObject dbObj = new BasicDBObject();
-        dbObj.put("test", "foo");
-        dbObj.put("nested", nested);
-        NestedPojo obj = conv.convertMongoToPojo(dbObj, NestedPojo.class);
-        assertEquals("foo", obj.getTest());
-        assertNull(obj.getIgnored());
-        assertNotNull(obj.getNested());
-        assertEquals("bar", obj.getNested().getTest());
-        assertEquals(null, obj.getNested().getIgnored());
-    }
-
-    @Test
-    public void testConvertIndexedPojoToMongo() {
-        MongoPojoConverter conv = new MongoPojoConverter();
-        IndexedPojo obj = new IndexedPojo();
-        obj.setTest("test");
-        SimplePojo obj1 = new SimplePojo();
-        obj1.setTest("test1");
-        SimplePojo obj2 = new SimplePojo();
-        obj2.setTest("test2");
-        SimplePojo obj3 = new SimplePojo();
-        obj3.setTest("test3");
-        obj.setIndexed(new SimplePojo[] { obj1, obj2, obj3 });
-
-        DBObject dbObject = conv.convertPojoToMongo(obj);
-        assertEquals(2, dbObject.keySet().size());
-        assertEquals("test", dbObject.get("test"));
-        List<?> indexedDbObj = (List<?>) dbObject.get("indexed");
-        assertEquals(3, indexedDbObj.size());
-
-        DBObject dbObj1 = (DBObject) indexedDbObj.get(0);
-        assertEquals(1, dbObj1.keySet().size());
-        assertEquals("test1", dbObj1.get("test"));
-
-        DBObject dbObj2 = (DBObject) indexedDbObj.get(1);
-        assertEquals(1, dbObj2.keySet().size());
-        assertEquals("test2", dbObj2.get("test"));
-
-        DBObject dbObj3 = (DBObject) indexedDbObj.get(2);
-        assertEquals(1, dbObj3.keySet().size());
-        assertEquals("test3", dbObj3.get("test"));
-    }
-
-    @Test
-    public void testConvertPrimitiveIndexedPojoToMongo() {
-        MongoPojoConverter conv = new MongoPojoConverter();
-        PrimitiveIndexedPojo obj = new PrimitiveIndexedPojo();
-        obj.setTest("test");
-        obj.setIndexed(new int[] { 1, 2, 3 });
-
-        DBObject dbObject = conv.convertPojoToMongo(obj);
-        assertEquals(2, dbObject.keySet().size());
-        assertEquals("test", dbObject.get("test"));
-        List<?> indexedDbObj = (List<?>) dbObject.get("indexed");
-        assertEquals(3, indexedDbObj.size());
-
-        assertEquals(1, indexedDbObj.get(0));
-        assertEquals(2, indexedDbObj.get(1));
-        assertEquals(3, indexedDbObj.get(2));
-
-    }
-
-    @Test
-    public void testConvertIndexedPojoFromMongo() {
-        MongoPojoConverter conv = new MongoPojoConverter();
-        DBObject indexed = new BasicDBObject();
-        indexed.put("test", "test");
-        DBObject dbObj1 = new BasicDBObject();
-        dbObj1.put("test", "test1");
-        DBObject dbObj2 = new BasicDBObject();
-        dbObj2.put("test", "test2");
-        DBObject dbObj3 = new BasicDBObject();
-        dbObj3.put("test", "test3");
-        indexed.put("indexed", Arrays.asList(dbObj1, dbObj2, dbObj3));
-
-        IndexedPojo obj = conv.convertMongoToPojo(indexed, IndexedPojo.class);
-        assertEquals("test", obj.getTest());
-        assertNull(obj.getIgnored());
-        assertNotNull(obj.getIndexed());
-        SimplePojo[] indexedObj = obj.getIndexed();
-        assertEquals("test1", indexedObj[0].getTest());
-        assertNull(indexedObj[0].getIgnored());
-        assertEquals("test2", indexedObj[1].getTest());
-        assertNull(indexedObj[1].getIgnored());
-        assertEquals("test3", indexedObj[2].getTest());
-        assertNull(indexedObj[2].getIgnored());
-    }
-
-    @Test
-    public void testConvertPrimitiveIndexedPojoFromMongo() {
-        MongoPojoConverter conv = new MongoPojoConverter();
-        DBObject indexed = new BasicDBObject();
-        indexed.put("test", "test");
-        indexed.put("indexed", Arrays.asList(1, 2, 3));
-
-        PrimitiveIndexedPojo obj = conv.convertMongoToPojo(indexed, PrimitiveIndexedPojo.class);
-        assertEquals("test", obj.getTest());
-        assertNull(obj.getIgnored());
-        assertNotNull(obj.getIndexed());
-        int[] indexedObj = obj.getIndexed();
-        assertEquals(1, indexedObj[0]);
-        assertEquals(2, indexedObj[1]);
-        assertEquals(3, indexedObj[2]);
-    }
-}
--- a/common/core/src/test/java/com/redhat/thermostat/common/storage/MongoQueryTest.java	Wed Nov 28 09:04:08 2012 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,107 +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;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import org.junit.Test;
-
-import com.mongodb.BasicDBObject;
-import com.mongodb.DBObject;
-import com.redhat.thermostat.common.storage.MongoQuery;
-import com.redhat.thermostat.storage.core.Category;
-import com.redhat.thermostat.storage.core.Query;
-import com.redhat.thermostat.storage.core.Query.Criteria;
-
-public class MongoQueryTest {
-
-    @Test
-    public void testEmptyQuery() {
-        MongoQuery query = new MongoQuery();
-        DBObject mongoQuery = query.getGeneratedQuery();
-        assertTrue(mongoQuery.keySet().isEmpty());
-    }
-
-    @Test
-    public void testCollectionName() {
-        MongoQuery query = new MongoQuery().from(new Category("some-collection"));
-        assertEquals("some-collection", query.getCategory().getName());
-    }
-
-    @Test
-    public void testWhereEquals() {
-        DBObject generatedQuery = generateSimpleWhereQuery("key", Criteria.EQUALS, "value");
-        assertEquals("value", generatedQuery.get("key"));
-    }
-
-    @Test
-    public void testWhereNotEquals() {
-        DBObject generatedQuery = generateSimpleWhereQuery("key", Criteria.NOT_EQUAL_TO, "value");
-        assertEquals(new BasicDBObject("$ne", "value"), generatedQuery.get("key"));
-    }
-
-    @Test
-    public void testWhereGreaterThan() {
-        DBObject generatedQuery = generateSimpleWhereQuery("key", Criteria.GREATER_THAN, "value");
-        assertEquals(new BasicDBObject("$gt", "value"), generatedQuery.get("key"));
-    }
-
-    @Test
-    public void testWhereGreaterThanOrEqualTo() {
-        DBObject generatedQuery = generateSimpleWhereQuery("key", Criteria.GREATER_THAN_OR_EQUAL_TO, "value");
-        assertEquals(new BasicDBObject("$gte", "value"), generatedQuery.get("key"));
-    }
-
-    @Test
-    public void testWhereLessThan() {
-        DBObject generatedQuery = generateSimpleWhereQuery("key", Criteria.LESS_THAN, "value");
-        assertEquals(new BasicDBObject("$lt", "value"), generatedQuery.get("key"));
-    }
-
-    @Test
-    public void testWhereLessThanOrEqualTo() {
-        DBObject generatedQuery = generateSimpleWhereQuery("key", Criteria.LESS_THAN_OR_EQUAL_TO, "value");
-        assertEquals(new BasicDBObject("$lte", "value"), generatedQuery.get("key"));
-    }
-
-    private DBObject generateSimpleWhereQuery(String key, Criteria criteria, Object value) {
-        MongoQuery query = new MongoQuery().where(key, criteria, value);
-        return query.getGeneratedQuery();
-    }
-
-}
--- a/common/core/src/test/java/com/redhat/thermostat/common/storage/MongoStorageTest.java	Wed Nov 28 09:04:08 2012 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,527 +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;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.same;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.util.UUID;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
-
-import com.mongodb.BasicDBObject;
-import com.mongodb.DB;
-import com.mongodb.DBCollection;
-import com.mongodb.DBCursor;
-import com.mongodb.DBObject;
-import com.mongodb.Mongo;
-import com.mongodb.MongoURI;
-import com.mongodb.gridfs.GridFS;
-import com.mongodb.gridfs.GridFSDBFile;
-import com.mongodb.gridfs.GridFSInputFile;
-import com.redhat.thermostat.common.storage.MongoConnection;
-import com.redhat.thermostat.common.storage.MongoQuery;
-import com.redhat.thermostat.common.storage.MongoStorage;
-import com.redhat.thermostat.storage.config.StartupConfiguration;
-import com.redhat.thermostat.storage.core.Category;
-import com.redhat.thermostat.storage.core.Cursor;
-import com.redhat.thermostat.storage.core.Entity;
-import com.redhat.thermostat.storage.core.Key;
-import com.redhat.thermostat.storage.core.Persist;
-import com.redhat.thermostat.storage.core.Query;
-import com.redhat.thermostat.storage.core.Update;
-import com.redhat.thermostat.storage.core.Query.Criteria;
-import com.redhat.thermostat.storage.core.Query.SortDirection;
-import com.redhat.thermostat.storage.model.BasePojo;
-
-@RunWith(PowerMockRunner.class)
-@PrepareForTest({ DBCollection.class, DB.class, Mongo.class, MongoStorage.class, MongoConnection.class })
-public class MongoStorageTest {
-
-    @Entity
-    public static class TestClass extends BasePojo {
-        private String key1;
-        private String key2;
-        private String key3;
-        private String key4;
-        private String key5;
-        @Persist
-        public String getKey1() {
-            return key1;
-        }
-        @Persist
-        public void setKey1(String key1) {
-            this.key1 = key1;
-        }
-        @Persist
-        public String getKey2() {
-            return key2;
-        }
-        @Persist
-        public void setKey2(String key2) {
-            this.key2 = key2;
-        }
-        @Persist
-        public String getKey3() {
-            return key3;
-        }
-        @Persist
-        public void setKey3(String key3) {
-            this.key3 = key3;
-        }
-        @Persist
-        public String getKey4() {
-            return key4;
-        }
-        @Persist
-        public void setKey4(String key4) {
-            this.key4 = key4;
-        }
-        @Persist
-        public String getKey5() {
-            return key5;
-        }
-        @Persist
-        public void setKey5(String key5) {
-            this.key5 = key5;
-        }
-    }
-
-    private static final Key<String> key1 = new Key<>("key1", true);
-    private static final Key<String> key2 = new Key<>("key2", true);
-    private static final Key<String> key3 = new Key<>("key3", false);
-    private static final Key<String> key4 = new Key<>("key4", false);
-    private static final Key<String> key5 = new Key<>("key5", false);
-    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 Mongo m;
-    private DB db;
-    private DBCollection testCollection, emptyTestCollection, mockedCollection;
-    private DBCursor cursor;
-
-    private MongoStorage makeStorage() {
-        MongoStorage storage = new MongoStorage(conf);
-        storage.mapCategoryToDBCollection(testCategory, testCollection);
-        storage.mapCategoryToDBCollection(emptyTestCategory, emptyTestCollection);
-        return storage;
-    }
-
-    @Before
-    public void setUp() throws Exception {
-        conf = mock(StartupConfiguration.class);
-        when(conf.getDBConnectionString()).thenReturn("mongodb://127.0.0.1:27518");
-        db = PowerMockito.mock(DB.class);
-        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");
-        value1.put("key2", "test2");
-        BasicDBObject value2 = new BasicDBObject();
-        value2.put("key3", "test3");
-        value2.put("key4", "test4");
-
-        cursor = mock(DBCursor.class);
-        when(cursor.hasNext()).thenReturn(true).thenReturn(true).thenReturn(false);
-        when(cursor.next()).thenReturn(value1).thenReturn(value2).thenReturn(null);
-
-        testCollection = PowerMockito.mock(DBCollection.class);
-        when(testCollection.find(any(DBObject.class))).thenReturn(cursor);
-        when(testCollection.find()).thenReturn(cursor);
-        when(testCollection.findOne(any(DBObject.class))).thenReturn(value1);
-        when(testCollection.getCount()).thenReturn(2L);
-        emptyTestCollection = PowerMockito.mock(DBCollection.class);
-        when(emptyTestCollection.getCount()).thenReturn(0L);
-        when(db.collectionExists(anyString())).thenReturn(false);
-        when(db.createCollection(anyString(), any(DBObject.class))).thenReturn(testCollection);
-    }
-
-    @After
-    public void tearDown() {
-        conf = null;
-        m = null;
-        db = null;
-        testCollection = null;
-        emptyTestCollection = null;
-        cursor = null;
-    }
-
-    @Test (expected=IllegalArgumentException.class)
-    public void verifyFindOnlyAcceptsMongoQuery() {
-        MongoStorage storage = makeStorage();
-        Query query = mock(Query.class);
-        storage.findPojo(query, TestClass.class);
-    }
-
-    @Test (expected=IllegalArgumentException.class)
-    public void verifyFindAllOnlyAcceptsMongoQuery() {
-        MongoStorage storage = makeStorage();
-        Query query = mock(Query.class);
-        storage.findAllPojos(query, TestClass.class);
-    }
-
-    @Test
-    public void verifyFindAllReturnsCursor() throws Exception {
-        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
-        MongoStorage storage = makeStorage();
-        Query query = storage.createQuery().from(testCategory);
-        Cursor<TestClass> cursor = storage.findAllPojos(query, TestClass.class);
-        assertNotNull(cursor);
-    }
-
-    @Test
-    public void verifyFindReturnsChunk() throws Exception {
-        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
-        MongoStorage storage = makeStorage();
-        Query query = storage.createQuery().from(testCategory).where(key1, Criteria.EQUALS, "test1");
-        TestClass result = storage.findPojo(query, TestClass.class);
-        assertNotNull(result);
-    }
-
-    @Test
-    public void verifyFindAllCallsDBCollectionFind() throws Exception {
-        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
-        MongoStorage storage = makeStorage();
-        Query query = storage.createQuery().from(testCategory).where(key1, Criteria.EQUALS, "fluff");
-        storage.findAllPojos(query, TestClass.class);
-        verify(testCollection).find(any(DBObject.class));
-    }
-
-    @Test
-    public void verifyFindCallsDBCollectionFindOne() throws Exception {
-        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
-        MongoStorage storage = makeStorage();
-        Query query = storage.createQuery().from(testCategory);
-        storage.findPojo(query, TestClass.class);
-        verify(testCollection).findOne(any(DBObject.class));
-    }
-
-    @Test
-    public void verifyFindAllCallsDBCollectionFindWithCorrectQuery() throws Exception {
-        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
-        MongoStorage storage = makeStorage();
-
-        MongoQuery query = mock(MongoQuery.class);
-        when(query.hasClauses()).thenReturn(true);
-        DBObject generatedQuery = mock(DBObject.class);
-        when(query.getGeneratedQuery()).thenReturn(generatedQuery);
-        when(query.getCategory()).thenReturn(testCategory);
-        ArgumentCaptor<DBObject> findArg = ArgumentCaptor.forClass(DBObject.class);
-
-        storage.findAllPojos(query, TestClass.class);
-
-        verify(testCollection).find(findArg.capture());
-        assertSame(generatedQuery, findArg.getValue());
-    }
-
-    @Test
-    public void verifyFindCallsDBCollectionFindOneWithCorrectQuery() throws Exception {
-        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
-        MongoStorage storage = makeStorage();
-
-        MongoQuery query = mock(MongoQuery.class);
-        DBObject generatedQuery = mock(DBObject.class);
-        when(query.getGeneratedQuery()).thenReturn(generatedQuery);
-        when(query.getCategory()).thenReturn(testCategory);
-
-        ArgumentCaptor<DBObject> findArg = ArgumentCaptor.forClass(DBObject.class);
-
-        storage.findPojo(query, TestClass.class);
-
-        verify(testCollection).findOne(findArg.capture());
-        assertSame(generatedQuery, findArg.getValue());
-    }
-
-    @Test
-    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
-        // Because we mock the DBCollection, the contents of this query don't actually determine the result.
-        MongoQuery query = new MongoQuery().from(testCategory);
-
-        TestClass result = storage.findPojo(query, TestClass.class);
-
-        assertNotNull(result);
-        assertEquals("test1", result.getKey1());
-        assertEquals("test2", result.getKey2());
-    }
-
-    @Test
-    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
-        // Because we mock the DBCollection, the contents of this query don't actually determine the result.
-        MongoQuery query = new MongoQuery().from(testCategory);
-
-        Cursor<TestClass> cursor = storage.findAllPojos(query, TestClass.class);
-
-        verifyDefaultCursor(cursor);
-    }
-
-    @Test
-    public void verifyFindAllWithSortAndLimit() 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
-        // Because we mock the DBCollection, the contents of this query don't actually determine the result.
-        MongoQuery query = (MongoQuery) new MongoQuery().from(testCategory).sort(key1, Query.SortDirection.ASCENDING).limit(3);
-
-        Cursor<TestClass> cursor = storage.findAllPojos(query, TestClass.class);
-
-        verifyDefaultCursor(cursor);
-        ArgumentCaptor<DBObject> orderBy = ArgumentCaptor.forClass(DBObject.class);
-        verify(this.cursor).sort(orderBy.capture());
-        assertTrue(orderBy.getValue().containsField("key1"));
-        assertEquals(1, orderBy.getValue().get("key1"));
-        verify(this.cursor).limit(3);
-    }
-
-    @Test
-    public void verifyFindAllFromCategoryCallsDBCollectionFindAll() throws Exception {
-        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
-        MongoStorage storage = makeStorage();
-        Query query = storage.createQuery().from(testCategory);
-        storage.findAllPojos(query, TestClass.class);
-        verify(testCollection).find();
-    }
-
-    @Test
-    public void verifyFindAllFromCategoryReturnsCorrectCursor() throws Exception {
-        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
-        MongoStorage storage = makeStorage();
-        Query query = storage.createQuery().from(testCategory);
-        Cursor<TestClass> cursor = storage.findAllPojos(query, TestClass.class);
-
-        verifyDefaultCursor(cursor);
-    }
-
-    @Test
-    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() 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() 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);
-    }
-
-    private void verifyDefaultCursor(Cursor<TestClass> cursor) {
-        assertTrue(cursor.hasNext());
-        TestClass obj1 = cursor.next();
-        assertEquals("test1", obj1.getKey1());
-        assertEquals("test2", obj1.getKey2());
-
-        assertTrue(cursor.hasNext());
-        TestClass obj2 = cursor.next();
-        assertEquals("test3", obj2.getKey3());
-        assertEquals("test4", obj2.getKey4());
-
-        assertFalse(cursor.hasNext());
-        assertNull(cursor.next());
-    }
-
-    @Test
-    public void verifySaveFile() throws Exception {
-        GridFSInputFile gridFSFile = mock(GridFSInputFile.class);
-        GridFS gridFS = mock(GridFS.class);
-        when(gridFS.createFile(any(InputStream.class), anyString())).thenReturn(gridFSFile);
-        PowerMockito.whenNew(GridFS.class).withArguments(any()).thenReturn(gridFS);
-        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
-        MongoStorage storage = makeStorage();
-        byte[] data = new byte[] { 1, 2, 3 };
-        InputStream dataStream = new ByteArrayInputStream(data);
-        storage.saveFile("test", dataStream);
-        verify(gridFS).createFile(same(dataStream), eq("test"));
-        verify(gridFSFile).save();
-    }
-
-    @Test
-    public void verifyPutChunkUsesCorrectChunkAgent() throws Exception {
-        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
-        MongoStorage storage = makeStorage();
-        TestClass pojo = new TestClass();
-        pojo.setAgentId("123");
-        storage.putPojo(testCategory, false, pojo);
-        ArgumentCaptor<DBObject> dbobj = ArgumentCaptor.forClass(DBObject.class);
-        verify(testCollection).insert(dbobj.capture());
-        DBObject val = dbobj.getValue();
-        assertEquals("123", val.get("agentId"));
-    }
-
-    @Test
-    public void verifyPutChunkUsesCorrectGlobalAgent() throws Exception {
-        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
-        MongoStorage storage = makeStorage();
-        storage.setAgentId(new UUID(1, 2));
-        TestClass pojo = new TestClass();
-        pojo.setAgentId("123");
-        storage.putPojo(testCategory, false, pojo);
-        ArgumentCaptor<DBObject> dbobj = ArgumentCaptor.forClass(DBObject.class);
-        verify(testCollection).insert(dbobj.capture());
-        DBObject val = dbobj.getValue();
-        assertEquals(new UUID(1, 2).toString(), val.get("agentId"));
-    }
-
-    @Test
-    public void verifyLoadFile() throws Exception {
-        InputStream stream = mock(InputStream.class);
-        GridFSDBFile file = mock(GridFSDBFile.class);
-        when(file.getInputStream()).thenReturn(stream);
-        GridFS gridFS = mock(GridFS.class);
-        when(gridFS.findOne("test")).thenReturn(file);
-        PowerMockito.whenNew(GridFS.class).withArguments(any()).thenReturn(gridFS);
-        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
-        MongoStorage storage = makeStorage();
-
-        InputStream actual = storage.loadFile("test");
-        assertSame(stream, actual);
-
-        actual = storage.loadFile("doesnotexist");
-        assertNull(actual);
-    }
-
-    @Test
-    public void verifySimpleUpdate() {
-        MongoStorage storage = makeStorage();
-        Update update = storage.createUpdate().from(testCategory).where(Key.AGENT_ID, "test1").set(key2, "test2");
-        storage.updatePojo(update);
-
-        ArgumentCaptor<DBObject> queryCaptor = ArgumentCaptor.forClass(DBObject.class);
-        ArgumentCaptor<DBObject> valueCaptor = ArgumentCaptor.forClass(DBObject.class);
-        
-        verify(testCollection).update(queryCaptor.capture(), valueCaptor.capture());
-        DBObject query = queryCaptor.getValue();
-        assertTrue(query.containsField(Key.AGENT_ID.getName()));
-        assertEquals("test1", query.get(Key.AGENT_ID.getName()));
-    }
-
-    @Test
-    public void verifyMultiFieldUpdate() {
-        MongoStorage storage = makeStorage();
-        Update update = storage.createUpdate().from(testCategory).where(Key.AGENT_ID, "test1").set(key2, "test2").set(key3, "test3");
-        storage.updatePojo(update);
-
-        ArgumentCaptor<DBObject> queryCaptor = ArgumentCaptor.forClass(DBObject.class);
-        ArgumentCaptor<DBObject> valueCaptor = ArgumentCaptor.forClass(DBObject.class);
-        
-        verify(testCollection).update(queryCaptor.capture(), valueCaptor.capture());
-        DBObject query = queryCaptor.getValue();
-        assertTrue(query.containsField(Key.AGENT_ID.getName()));
-        assertEquals("test1", query.get(Key.AGENT_ID.getName()));
-        DBObject value = valueCaptor.getValue();
-        assertTrue(value.containsField("$set"));
-        DBObject values = (DBObject) value.get("$set");
-        assertTrue(values.containsField("key2"));
-        assertEquals("test2", values.get("key2"));
-        assertTrue(values.containsField("key3"));
-        assertEquals("test3", values.get("key3"));
-    }
-
-    @Test
-    public void verifyInsertReplaceCallsUpdate() {
-        TestClass pojo = new TestClass();
-        pojo.setAgentId("123");
-        pojo.setKey1("test1");
-        pojo.setKey2("test2");
-        pojo.setKey3("test3");
-        pojo.setKey4("test4");
-        pojo.setKey5("test5");
-
-        MongoStorage storage = makeStorage();
-        storage.putPojo(testCategory, true, pojo);
-
-        ArgumentCaptor<DBObject> queryCaptor = ArgumentCaptor.forClass(DBObject.class);
-        ArgumentCaptor<DBObject> valueCaptor = ArgumentCaptor.forClass(DBObject.class);
-        verify(testCollection).update(queryCaptor.capture(), valueCaptor.capture(), eq(true), eq(false));
-
-        DBObject query = queryCaptor.getValue();
-        assertEquals(2, query.keySet().size());
-        assertEquals("test1", query.get("key1"));
-        assertEquals("test2", query.get("key2"));
-
-        DBObject value = valueCaptor.getValue();
-        assertEquals(6, value.keySet().size());
-        assertEquals("test1", value.get("key1"));
-        assertEquals("test2", value.get("key2"));
-        assertEquals("test3", value.get("key3"));
-        assertEquals("test4", value.get("key4"));
-        assertEquals("test5", value.get("key5"));
-        assertEquals("123", value.get("agentId"));
-    }
-}
--- a/common/core/src/test/java/com/redhat/thermostat/common/storage/QueryTestHelper.java	Wed Nov 28 09:04:08 2012 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +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;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import com.redhat.thermostat.storage.core.Category;
-import com.redhat.thermostat.storage.core.Key;
-import com.redhat.thermostat.storage.core.Query;
-import com.redhat.thermostat.storage.core.Remove;
-import com.redhat.thermostat.storage.core.Update;
-import com.redhat.thermostat.storage.core.Query.Criteria;
-
-public class QueryTestHelper {
-
-    @SuppressWarnings("unchecked")
-    public static Update createMockUpdate() {
-        Update mockUpdate = mock(Update.class);
-        when(mockUpdate.from(any(Category.class))).thenReturn(mockUpdate);
-        when(mockUpdate.where(any(Key.class), any())).thenReturn(mockUpdate);
-        when(mockUpdate.set(any(Key.class), any())).thenReturn(mockUpdate);
-        return mockUpdate;
-    }
-
-    @SuppressWarnings("unchecked")
-    public static Remove createMockRemove() {
-        Remove mockRemove = mock(Remove.class);
-        when(mockRemove.from(any(Category.class))).thenReturn(mockRemove);
-        when(mockRemove.where(any(Key.class), any())).thenReturn(mockRemove);
-        return mockRemove;
-    }
-
-    @SuppressWarnings("unchecked")
-    public static Query createMockQuery() {
-        Query mockQuery = mock(Query.class);
-        when(mockQuery.from(any(Category.class))).thenReturn(mockQuery);
-        when(mockQuery.where(any(Key.class), any(Criteria.class), any())).thenReturn(mockQuery);
-        return mockQuery;
-    }
-}
--- a/distribution/config/commands/agent.properties	Wed Nov 28 09:04:08 2012 -0500
+++ b/distribution/config/commands/agent.properties	Tue Nov 27 14:29:35 2012 +0100
@@ -16,6 +16,11 @@
           thermostat-system-backend-@project.version@.jar, \
           thermostat-gc-remote-collector-common-@project.version@.jar, \
           thermostat-gc-remote-collector-command-@project.version@.jar, \
+          thermostat-storage-mongodb-${project.version}.jar, \
+          mongo.jar, \
+          commons-beanutils.jar, \
+          commons-collections.jar, \
+          commons-logging.jar, \
           netty.jar
 
 description = starts and stops the thermostat agent
--- a/distribution/config/commands/connect.properties	Wed Nov 28 09:04:08 2012 -0500
+++ b/distribution/config/commands/connect.properties	Tue Nov 27 14:29:35 2012 +0100
@@ -2,6 +2,11 @@
 # In order to support web storage connections we add web bundles here
 bundles = thermostat-web-common-0.5.0-SNAPSHOT.jar, \
           thermostat-web-client-0.5.0-SNAPSHOT.jar, \
+          thermostat-storage-mongodb-${project.version}.jar, \
+          mongo.jar, \
+          commons-beanutils.jar, \
+          commons-collections.jar, \
+          commons-logging.jar, \
           httpcomponents-core.jar, \
           httpcomponents-client.jar, \
           gson.jar
--- a/distribution/config/commands/dump-heap.properties	Wed Nov 28 09:04:08 2012 -0500
+++ b/distribution/config/commands/dump-heap.properties	Tue Nov 27 14:29:35 2012 +0100
@@ -4,6 +4,11 @@
           thermostat-client-command-@project.version@.jar, \
           thermostat-client-heapdumper-core-@project.version@.jar, \
           thermostat-laf-@project.version@.jar, \
+          thermostat-storage-mongodb-${project.version}.jar, \
+          mongo.jar, \
+          commons-beanutils.jar, \
+          commons-collections.jar, \
+          commons-logging.jar, \
           netty.jar
 
 description = trigger a heap dump on the VM
--- a/distribution/config/commands/find-objects.properties	Wed Nov 28 09:04:08 2012 -0500
+++ b/distribution/config/commands/find-objects.properties	Tue Nov 27 14:29:35 2012 +0100
@@ -1,9 +1,13 @@
-bundles = thermostat-common-core-@project.version@.jar, \
-          thermostat-client-core-@project.version@.jar, \
+bundles = thermostat-client-core-@project.version@.jar, \
           thermostat-common-command-@project.version@.jar, \
           thermostat-client-command-@project.version@.jar, \
           thermostat-client-heapdumper-core-@project.version@.jar, \
           thermostat-laf-@project.version@.jar, \
+          thermostat-storage-mongodb-${project.version}.jar, \
+          mongo.jar, \
+          commons-beanutils.jar, \
+          commons-collections.jar, \
+          commons-logging.jar, \
           netty.jar
 
 description = Finds objects in a heapdump
--- a/distribution/config/commands/find-root.properties	Wed Nov 28 09:04:08 2012 -0500
+++ b/distribution/config/commands/find-root.properties	Tue Nov 27 14:29:35 2012 +0100
@@ -1,9 +1,13 @@
-bundles = thermostat-common-core-@project.version@.jar, \
-          thermostat-client-core-@project.version@.jar, \
+bundles = thermostat-client-core-@project.version@.jar, \
           thermostat-common-command-@project.version@.jar, \
           thermostat-client-command-@project.version@.jar, \
           thermostat-client-heapdumper-core-@project.version@.jar, \
           thermostat-laf-@project.version@.jar, \
+          thermostat-storage-mongodb-${project.version}.jar, \
+          mongo.jar, \
+          commons-beanutils.jar, \
+          commons-collections.jar, \
+          commons-logging.jar, \
           netty.jar
 
 description = finds the shortest path from an object to a GC root
--- a/distribution/config/commands/gui.properties	Wed Nov 28 09:04:08 2012 -0500
+++ b/distribution/config/commands/gui.properties	Tue Nov 27 14:29:35 2012 +0100
@@ -1,4 +1,8 @@
-bundles = thermostat-common-core-@project.version@.jar, \
+bundles = thermostat-storage-mongodb-${project.version}.jar, \
+          mongo.jar, \
+          commons-beanutils.jar, \
+          commons-collections.jar, \
+          commons-logging.jar, \
           gson.jar, \
           thermostat-web-common-@project.version@.jar, \
           thermostat-web-client-@project.version@.jar, \
--- a/distribution/config/commands/list-heap-dumps.properties	Wed Nov 28 09:04:08 2012 -0500
+++ b/distribution/config/commands/list-heap-dumps.properties	Tue Nov 27 14:29:35 2012 +0100
@@ -1,4 +1,8 @@
-bundles = thermostat-common-core-@project.version@.jar, \
+bundles = thermostat-storage-mongodb-${project.version}.jar, \
+          mongo.jar, \
+          commons-beanutils.jar, \
+          commons-collections.jar, \
+          commons-logging.jar, \
           thermostat-client-core-@project.version@.jar, \
           thermostat-common-command-@project.version@.jar, \
           thermostat-client-command-@project.version@.jar, \
--- a/distribution/config/commands/list-vms.properties	Wed Nov 28 09:04:08 2012 -0500
+++ b/distribution/config/commands/list-vms.properties	Tue Nov 27 14:29:35 2012 +0100
@@ -1,5 +1,9 @@
-# ListVmsCommand is provided by tools bundle (a bootstrap bundle) and needs no other bundles
-bundles =
+# ListVmsCommand is provided by tools bundle (a bootstrap bundle), but needs storage impls
+bundles = thermostat-storage-mongodb-${project.version}.jar, \
+          mongo.jar, \
+          commons-beanutils.jar, \
+          commons-collections.jar, \
+          commons-logging.jar, \
 
 description = lists all currently monitored VMs
 
--- a/distribution/config/commands/object-info.properties	Wed Nov 28 09:04:08 2012 -0500
+++ b/distribution/config/commands/object-info.properties	Tue Nov 27 14:29:35 2012 +0100
@@ -1,4 +1,8 @@
-bundles = thermostat-common-core-@project.version@.jar, \
+bundles = thermostat-storage-mongodb-${project.version}.jar, \
+          mongo.jar, \
+          commons-beanutils.jar, \
+          commons-collections.jar, \
+          commons-logging.jar, \
           thermostat-client-core-@project.version@.jar, \
           thermostat-common-command-@project.version@.jar, \
           thermostat-client-command-@project.version@.jar, \
--- a/distribution/config/commands/ping.properties	Wed Nov 28 09:04:08 2012 -0500
+++ b/distribution/config/commands/ping.properties	Tue Nov 27 14:29:35 2012 +0100
@@ -1,5 +1,10 @@
 bundles = thermostat-common-command-@project.version@.jar, \
           thermostat-client-command-@project.version@.jar, \
+          thermostat-storage-mongodb-${project.version}.jar, \
+          mongo.jar, \
+          commons-beanutils.jar, \
+          commons-collections.jar, \
+          commons-logging.jar, \
           netty.jar
 
 description = using the Command Channel, send a ping to a running agent
--- a/distribution/config/commands/save-heap-dump-to-file.properties	Wed Nov 28 09:04:08 2012 -0500
+++ b/distribution/config/commands/save-heap-dump-to-file.properties	Tue Nov 27 14:29:35 2012 +0100
@@ -1,4 +1,8 @@
-bundles = thermostat-common-core-@project.version@.jar, \
+bundles = thermostat-storage-mongodb-${project.version}.jar, \
+          mongo.jar, \
+          commons-beanutils.jar, \
+          commons-collections.jar, \
+          commons-logging.jar, \
           thermostat-client-core-@project.version@.jar, \
           thermostat-common-command-@project.version@.jar, \
           thermostat-client-command-@project.version@.jar, \
--- a/distribution/config/commands/service.properties	Wed Nov 28 09:04:08 2012 -0500
+++ b/distribution/config/commands/service.properties	Tue Nov 27 14:29:35 2012 +0100
@@ -1,4 +1,9 @@
 bundles = thermostat-agent-core-@project.version@.jar, \
+          thermostat-storage-mongodb-${project.version}.jar, \
+          mongo.jar, \
+          commons-beanutils.jar, \
+          commons-collections.jar, \
+          commons-logging.jar, \
           thermostat-osgi-process-handler-@project.version@.jar, \
           thermostat-common-command-@project.version@.jar, \
           thermostat-agent-command-@project.version@.jar, \
--- a/distribution/config/commands/show-heap-histogram.properties	Wed Nov 28 09:04:08 2012 -0500
+++ b/distribution/config/commands/show-heap-histogram.properties	Tue Nov 27 14:29:35 2012 +0100
@@ -1,4 +1,8 @@
-bundles = thermostat-common-core-@project.version@.jar, \
+bundles = thermostat-storage-mongodb-${project.version}.jar, \
+          mongo.jar, \
+          commons-beanutils.jar, \
+          commons-collections.jar, \
+          commons-logging.jar, \
           thermostat-client-core-@project.version@.jar, \
           thermostat-common-command-@project.version@.jar, \
           thermostat-client-command-@project.version@.jar, \
--- a/distribution/config/commands/storage.properties	Wed Nov 28 09:04:08 2012 -0500
+++ b/distribution/config/commands/storage.properties	Tue Nov 27 14:29:35 2012 +0100
@@ -1,6 +1,5 @@
 bundles = thermostat-agent-core-@project.version@.jar, \
           thermostat-osgi-process-handler-@project.version@.jar, \
-          thermostat-common-core-@project.version@.jar, \
           thermostat-agent-cli-@project.version@.jar, \
           thermostat-common-command-@project.version@.jar, \
           thermostat-agent-command-@project.version@.jar, \
--- a/distribution/config/commands/vm-info.properties	Wed Nov 28 09:04:08 2012 -0500
+++ b/distribution/config/commands/vm-info.properties	Tue Nov 27 14:29:35 2012 +0100
@@ -1,5 +1,10 @@
-# VMInfoCommand is provided by the tools bundle, a bootstrap bundle, and requires no other bundles.
-bundles =
+# VMInfoCommand is provided by the tools bundle, a bootstrap bundle.
+# Requires storage impls only.
+bundles = thermostat-storage-mongodb-${project.version}.jar, \
+          mongo.jar, \
+          commons-beanutils.jar, \
+          commons-collections.jar, \
+          commons-logging.jar
 
 description = shows basic information about a VM
 
--- a/distribution/config/commands/vm-stat.properties	Wed Nov 28 09:04:08 2012 -0500
+++ b/distribution/config/commands/vm-stat.properties	Tue Nov 27 14:29:35 2012 +0100
@@ -1,5 +1,10 @@
-# VMStatCommand is provided by tools bundle, a bootstrap bundle, and requires no other bundles.
-bundles =
+# VMStatCommand is provided by tools bundle, a bootstrap bundle.
+# Requires storage impl bundles only.
+bundles = thermostat-storage-mongodb-${project.version}.jar, \
+          mongo.jar, \
+          commons-beanutils.jar, \
+          commons-collections.jar, \
+          commons-logging.jar
 
 description = show various statistics about a VM
 
--- a/distribution/config/commands/webservice.properties	Wed Nov 28 09:04:08 2012 -0500
+++ b/distribution/config/commands/webservice.properties	Tue Nov 27 14:29:35 2012 +0100
@@ -1,7 +1,14 @@
+# Needs at least one implementing storage back-end (e.g. mongodb),
+# since it builds on top of this implementing back-end.
 bundles = thermostat-web-cmd-@project.version@.jar, \
           thermostat-web-server-@project.version@.jar, \
           thermostat-web-common-@project.version@.jar, \
           thermostat-thread-collector-@project.version@.jar, \
+          thermostat-storage-mongodb-${project.version}.jar, \
+          mongo.jar, \
+          commons-beanutils.jar, \
+          commons-collections.jar, \
+          commons-logging.jar, \
           commons-fileupload.jar, \
           commons-io.jar, \
           gson.jar, \
--- a/eclipse/com.redhat.thermostat.client.feature/feature.xml	Wed Nov 28 09:04:08 2012 -0500
+++ b/eclipse/com.redhat.thermostat.client.feature/feature.xml	Tue Nov 27 14:29:35 2012 +0100
@@ -49,6 +49,13 @@
 to do so, delete this exception statement from your version.
    </license>
 
+   <requires>
+      <import plugin="org.eclipse.orbit.mongodb" version="2.7.3" match="greaterOrEqual"/>
+      <import plugin="org.eclipse.osgi"/>
+      <import plugin="org.apache.commons.beanutils" version="1.8.0" match="greaterOrEqual"/>
+      <import plugin="org.apache.commons.cli" version="1.2.0" match="greaterOrEqual"/>
+   </requires>
+
    <plugin
          id="com.redhat.thermostat.client.core"
          download-size="0"
@@ -161,4 +168,11 @@
          version="0.0.0"
          unpack="false"/>
 
+   <plugin
+         id="com.redhat.thermostat.storage.mongodb"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
 </feature>
--- a/eclipse/com.redhat.thermostat.client.feature/pom.xml	Wed Nov 28 09:04:08 2012 -0500
+++ b/eclipse/com.redhat.thermostat.client.feature/pom.xml	Tue Nov 27 14:29:35 2012 +0100
@@ -61,6 +61,11 @@
     </dependency>
     <dependency>
       <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-storage-mongodb</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
       <artifactId>thermostat-storage-core</artifactId>
       <version>0.5.0-SNAPSHOT</version>
     </dependency>
--- a/eclipse/com.redhat.thermostat.eclipse/META-INF/MANIFEST.MF	Wed Nov 28 09:04:08 2012 -0500
+++ b/eclipse/com.redhat.thermostat.eclipse/META-INF/MANIFEST.MF	Tue Nov 27 14:29:35 2012 +0100
@@ -9,8 +9,7 @@
 Bundle-ActivationPolicy: lazy
 Require-Bundle: org.eclipse.core.runtime,
  org.eclipse.ui
-Import-Package: com.mongodb,
- com.redhat.thermostat.client.core.views,
+Import-Package: com.redhat.thermostat.client.core.views,
  com.redhat.thermostat.client.locale,
  com.redhat.thermostat.client.ui,
  com.redhat.thermostat.common,
--- a/eclipse/com.redhat.thermostat.eclipse/pom.xml	Wed Nov 28 09:04:08 2012 -0500
+++ b/eclipse/com.redhat.thermostat.eclipse/pom.xml	Tue Nov 27 14:29:35 2012 +0100
@@ -19,6 +19,7 @@
       <groupId>com.redhat.thermostat.eclipse.parent</groupId>
       <artifactId>com.redhat.thermostat.client.feature</artifactId>
       <version>0.5.0-SNAPSHOT</version>
+      <type>eclipse-feature</type>
     </dependency>
   </dependencies>
 
--- a/main/src/main/resources/com/redhat/thermostat/main/impl/bootstrapbundles.properties	Wed Nov 28 09:04:08 2012 -0500
+++ b/main/src/main/resources/com/redhat/thermostat/main/impl/bootstrapbundles.properties	Tue Nov 27 14:29:35 2012 +0100
@@ -7,8 +7,4 @@
         thermostat-main-${project.version}.jar, \
         jline2.jar, \
         commons-cli.jar, \
-        commons-beanutils.jar, \
-        commons-collections.jar, \
-        commons-logging.jar, \
-        lucene.jar, \
-        mongo.jar
+        lucene.jar
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/config/AuthenticationConfiguration.java	Tue Nov 27 14:29:35 2012 +0100
@@ -0,0 +1,43 @@
+/*
+ * 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.storage.config;
+
+public interface AuthenticationConfiguration {
+
+    String getUsername();
+    String getPassword();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/config/ConnectionConfiguration.java	Tue Nov 27 14:29:35 2012 +0100
@@ -0,0 +1,66 @@
+/*
+ * 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.storage.config;
+
+
+public class ConnectionConfiguration implements StartupConfiguration, AuthenticationConfiguration {
+
+    private String dbUrl;
+    private String username;
+    private String password;
+
+    public ConnectionConfiguration(String dbUrl, String username, String password) {
+        this.dbUrl = dbUrl;
+        this.username = username;
+        this.password = password;
+    }
+
+    @Override
+    public String getDBConnectionString() {
+        return dbUrl;
+    }
+
+    @Override
+    public String getUsername() {
+        return username;
+    }
+
+    @Override
+    public String getPassword() {
+        return password;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/mongo/pom.xml	Tue Nov 27 14:29:35 2012 +0100
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+
+ 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.redhat.thermostat</groupId>
+    <artifactId>thermostat-storage</artifactId>
+    <version>0.5.0-SNAPSHOT</version>
+  </parent>
+  
+  <artifactId>thermostat-storage-mongodb</artifactId>
+  <packaging>bundle</packaging>
+
+  <name>Thermostat Storage Library for MongoDB</name>
+  <url>${project.parent.url}</url>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-SymbolicName>com.redhat.thermostat.storage.mongodb</Bundle-SymbolicName>
+            <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
+            <Bundle-Activator>com.redhat.thermostat.storage.mongodb.internal.Activator</Bundle-Activator>
+            <Private-Package>
+              com.redhat.thermostat.storage.mongodb.internal
+            </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  
+  <dependencies>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.felix</groupId>
+      <artifactId>org.apache.felix.framework</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.mongodb</groupId>
+      <artifactId>mongo-java-driver</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-beanutils</groupId>
+      <artifactId>commons-beanutils</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-collections</groupId>
+      <artifactId>commons-collections</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>com.springsource.org.apache.commons.logging</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.powermock</groupId>
+      <artifactId>powermock-api-mockito</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.powermock</groupId>
+      <artifactId>powermock-module-junit4</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+    	<groupId>com.redhat.thermostat</groupId>
+    	<artifactId>thermostat-storage-core</artifactId>
+    	<version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/Activator.java	Tue Nov 27 14:29:35 2012 +0100
@@ -0,0 +1,25 @@
+package com.redhat.thermostat.storage.mongodb.internal;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+import com.redhat.thermostat.storage.core.StorageProvider;
+
+public class Activator implements BundleActivator {
+
+    @SuppressWarnings("rawtypes")
+    private ServiceRegistration reg;
+    
+    @Override
+    public void start(BundleContext context) throws Exception {
+        StorageProvider prov = new MongoStorageProvider();
+        reg = context.registerService(StorageProvider.class.getName(), prov, null);
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        reg.unregister();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoConnection.java	Tue Nov 27 14:29:35 2012 +0100
@@ -0,0 +1,121 @@
+/*
+ * 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.storage.mongodb.internal;
+
+import java.io.IOException;
+import java.net.UnknownHostException;
+
+import com.mongodb.DB;
+import com.mongodb.Mongo;
+import com.mongodb.MongoException;
+import com.mongodb.MongoURI;
+import com.redhat.thermostat.storage.config.AuthenticationConfiguration;
+import com.redhat.thermostat.storage.config.StartupConfiguration;
+import com.redhat.thermostat.storage.core.Connection;
+import com.redhat.thermostat.storage.core.ConnectionException;
+import com.redhat.thermostat.storage.core.StorageConstants;
+
+class MongoConnection extends Connection {
+
+    private Mongo m = null;
+    private DB db = null;
+    private StartupConfiguration conf;
+
+    MongoConnection(StartupConfiguration conf) {
+        this.conf = conf;
+    }
+
+    @Override
+    public void connect() {
+        try {
+            createConnection();
+            authenticateIfNecessary();
+            /* the mongo java driver does not ensure this connection is actually working */
+            testConnection();
+            connected = true;
+
+        } catch (IOException | MongoException | IllegalArgumentException e) {
+            fireChanged(ConnectionStatus.FAILED_TO_CONNECT);
+            throw new ConnectionException(e.getMessage(), e);
+        }
+        fireChanged(ConnectionStatus.CONNECTED);
+    }
+
+    private void authenticateIfNecessary() {
+        if (conf instanceof AuthenticationConfiguration) {
+            AuthenticationConfiguration authConf = (AuthenticationConfiguration) conf;
+            String username = authConf.getUsername();
+            if (username != null && ! username.equals("")) {
+                authenticate(username, authConf.getPassword());
+            }
+        }
+    }
+
+    private void authenticate(String username, String password) {
+        if (! db.authenticate(username, password.toCharArray())) {
+            throw new MongoException("Invalid username/password");
+        }
+    }
+
+    @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() {
+        String url = conf.getDBConnectionString();
+        MongoURI uri = new MongoURI(url);
+        return uri;
+    }
+
+    private void testConnection() {
+        db.getCollection("agent-config").getCount();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoCursor.java	Tue Nov 27 14:29:35 2012 +0100
@@ -0,0 +1,69 @@
+/*
+ * 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.storage.mongodb.internal;
+
+import com.mongodb.DBCursor;
+import com.mongodb.DBObject;
+import com.redhat.thermostat.storage.core.Cursor;
+import com.redhat.thermostat.storage.model.Pojo;
+
+class MongoCursor<T extends Pojo> implements Cursor<T> {
+
+    private DBCursor cursor;
+    private Class<T> resultClass;
+
+    MongoCursor(DBCursor cursor, Class<T> resultClass) {
+        this.cursor = cursor;
+        this.resultClass = resultClass;
+    }
+
+    @Override
+    public boolean hasNext() {
+        return cursor.hasNext();
+    }
+
+    @Override
+    public T next() {
+        DBObject next = cursor.next();
+        if (next == null) {
+            return null;
+        }
+        MongoPojoConverter converter = new MongoPojoConverter();
+        return converter.convertMongoToPojo(next, resultClass);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoPojoConverter.java	Tue Nov 27 14:29:35 2012 +0100
@@ -0,0 +1,166 @@
+/*
+ * 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.storage.mongodb.internal;
+
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.beanutils.PropertyUtils;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBObject;
+import com.redhat.thermostat.storage.core.Persist;
+import com.redhat.thermostat.storage.core.StorageException;
+import com.redhat.thermostat.storage.model.Pojo;
+
+class MongoPojoConverter {
+
+    public DBObject convertPojoToMongo(Pojo obj) {
+        try {
+            return convertPojoToMongoImpl(obj);
+        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException ex) {
+            throw new StorageException(ex);
+        }
+    }
+
+    private DBObject convertPojoToMongoImpl(Pojo obj) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+        BasicDBObject dbObj = new BasicDBObject();
+        PropertyDescriptor[] descs = PropertyUtils.getPropertyDescriptors(obj);
+        for (PropertyDescriptor desc : descs) {
+            storePropertyToDBObject(obj, dbObj, desc);
+        }
+        return dbObj;
+    }
+
+    private void storePropertyToDBObject(Pojo obj, BasicDBObject dbObj, PropertyDescriptor desc) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+        if (hasPersistentAnnotation(desc)) {
+            String name = desc.getName();
+            Object value = PropertyUtils.getProperty(obj, name);
+            if (desc.getPropertyType().isArray()) {
+                value = convertIndexedProperty(value);
+            }
+            if (value instanceof Pojo) {
+                value = convertPojoToMongoImpl((Pojo) value);
+            }
+            dbObj.put(name, value);
+        }
+    }
+
+    private Object convertIndexedProperty(Object values) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+        int length = Array.getLength(values);
+        List list = new ArrayList(length);
+        for (int i = 0; i < length; i++) {
+            Object value = Array.get(values, i);
+            if (value instanceof Pojo) {
+                value = convertPojoToMongoImpl((Pojo) value);
+            }
+            list.add(value);
+        }
+        return list;
+    }
+
+    public <T extends Pojo> T convertMongoToPojo(DBObject dbObj, Class<T> pojoClass) {
+        try {
+            return convertMongoToPojoImpl(dbObj, pojoClass);
+        } catch (IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException ex) {
+            throw new StorageException(ex);
+        }
+    }
+
+    private <T extends Pojo> T convertMongoToPojoImpl(DBObject dbObj, Class pojoClass) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+        if (dbObj == null) {
+            return null;
+        }
+        T pojo = (T) pojoClass.newInstance();
+        Set<String> keys = dbObj.keySet();
+        for (String name : keys) {
+            if (! name.equals("_id")) {
+                storePropertyToPojo(dbObj, pojo, name);
+            }
+        }
+        return pojo;
+    }
+
+    private <T extends Pojo> void storePropertyToPojo(DBObject dbObj, T pojo, String name)
+            throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, InstantiationException {
+
+        PropertyDescriptor desc = PropertyUtils.getPropertyDescriptor(pojo, name);
+        if (hasPersistentAnnotation(desc)) {
+            Object value = dbObj.get(name);
+            if (desc.getPropertyType().isArray()) {
+                value = convertIndexedPropertyFromMongo(desc, (List) value);
+            }
+            if (value instanceof DBObject) {
+                value = convertMongoToPojoImpl((DBObject) value, desc.getPropertyType());
+            }
+            PropertyUtils.setProperty(pojo, name, value);
+        } else {
+            throw new StorageException("no available mapping for extra property: '" + name + "' in " + pojo.getClass().getName());
+        }
+    }
+
+    private Object convertIndexedPropertyFromMongo(PropertyDescriptor desc, List values) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+        Class componentType = desc.getPropertyType().getComponentType();
+        Object array = Array.newInstance(componentType, values.size());
+        int i = 0;
+        for (Object value : values) {
+            if (value instanceof DBObject) {
+                value = convertMongoToPojoImpl((DBObject) value, componentType);
+            }
+            Array.set(array, i, value);
+            i++;
+        }
+        return array;
+    }
+
+    private boolean hasPersistentAnnotation(PropertyDescriptor desc) {
+        if (desc == null) {
+            return false;
+        }
+        Method writeMethod = desc.getWriteMethod();
+        Method readMethod = desc.getReadMethod();
+        return writeMethod != null && writeMethod.isAnnotationPresent(Persist.class)
+               && readMethod != null && readMethod.isAnnotationPresent(Persist.class);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoQuery.java	Tue Nov 27 14:29:35 2012 +0100
@@ -0,0 +1,131 @@
+/*
+ * 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.storage.mongodb.internal;
+
+import java.util.Objects;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBObject;
+import com.redhat.thermostat.storage.core.AbstractQuery;
+import com.redhat.thermostat.storage.core.Category;
+import com.redhat.thermostat.storage.core.Key;
+
+public class MongoQuery extends AbstractQuery {
+
+    private BasicDBObject query = new BasicDBObject();
+    private boolean hasClauses = false;
+    private Category category;
+
+    @Override
+    public MongoQuery from(Category category) {
+        setCategory(category);
+        return this;
+    }
+
+    public Category getCategory() {
+        return category;
+    }
+
+    public void setCategory(Category category) {
+        this.category = category;
+    }
+
+    @Override
+    public <T> MongoQuery where(Key<T> key, Criteria operator, T value) {
+        return where(key.getName(), operator, value);
+    }
+
+    public MongoQuery where(String key, Criteria operator, Object value) {
+        switch (operator) {
+        case EQUALS:
+            query.put(key, value);
+            break;
+
+        case NOT_EQUAL_TO:
+            query.put(key, new BasicDBObject("$ne", value));
+            break;
+
+        case LESS_THAN:
+            query.put(key, new BasicDBObject("$lt", value));
+            break;
+
+        case LESS_THAN_OR_EQUAL_TO:
+            query.put(key, new BasicDBObject("$lte", value));
+            break;
+        case GREATER_THAN:
+            query.put(key, new BasicDBObject("$gt", value));
+            break;
+
+        case GREATER_THAN_OR_EQUAL_TO:
+            query.put(key, new BasicDBObject("$gte", value));
+            break;
+        default:
+            throw new IllegalArgumentException("MongoQuery can not handle " + operator);
+        }
+        hasClauses = true;
+        return this;
+    }
+
+    DBObject getGeneratedQuery() {
+        return query;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof MongoQuery)) {
+            return false;
+        }
+        MongoQuery other = (MongoQuery) obj;
+        return Objects.equals(getCategory(), other.getCategory()) && Objects.equals(this.query, other.query);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getCategory(), this.query);
+    }
+
+    boolean hasClauses() {
+        return hasClauses ;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoRemove.java	Tue Nov 27 14:29:35 2012 +0100
@@ -0,0 +1,77 @@
+/*
+ * 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.storage.mongodb.internal;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBObject;
+import com.redhat.thermostat.storage.core.Category;
+import com.redhat.thermostat.storage.core.Key;
+import com.redhat.thermostat.storage.core.Remove;
+
+class MongoRemove implements Remove {
+
+    private Category category;
+    private DBObject query;
+
+    @Override
+    public Remove from(Category category) {
+        if (query != null) {
+            throw new IllegalStateException();
+        }
+        this.category = category;
+        return this;
+    }
+
+    Category getCategory() {
+        return category;
+    }
+
+    @Override
+    public <T> Remove where(Key<T> key, T value) {
+        if (query == null) {
+            query = new BasicDBObject();
+        }
+        query.put(key.getName(), value);
+        return this;
+    }
+
+    DBObject getQuery() {
+        return query;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoStorage.java	Tue Nov 27 14:29:35 2012 +0100
@@ -0,0 +1,315 @@
+/*
+ * 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.storage.mongodb.internal;
+
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DB;
+import com.mongodb.DBCollection;
+import com.mongodb.DBCursor;
+import com.mongodb.DBObject;
+import com.mongodb.gridfs.GridFS;
+import com.mongodb.gridfs.GridFSDBFile;
+import com.mongodb.gridfs.GridFSInputFile;
+import com.redhat.thermostat.storage.config.StartupConfiguration;
+import com.redhat.thermostat.storage.core.AbstractQuery.Sort;
+import com.redhat.thermostat.storage.core.Category;
+import com.redhat.thermostat.storage.core.Connection;
+import com.redhat.thermostat.storage.core.Connection.ConnectionListener;
+import com.redhat.thermostat.storage.core.Connection.ConnectionStatus;
+import com.redhat.thermostat.storage.core.Cursor;
+import com.redhat.thermostat.storage.core.Key;
+import com.redhat.thermostat.storage.core.Query;
+import com.redhat.thermostat.storage.core.Remove;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.core.Update;
+import com.redhat.thermostat.storage.model.AgentIdPojo;
+import com.redhat.thermostat.storage.model.Pojo;
+
+/**
+ * Implementation of the Storage interface that uses MongoDB to store the instrumentation data.
+ *
+ * In this implementation, each CATEGORY is given a distinct collection.
+ */
+public class MongoStorage implements Storage {
+
+    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;
+
+    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
+                }
+            }
+        });
+    }
+
+    @Override
+    public Connection getConnection() {
+        return conn;
+    }
+
+    @Override
+    public void setAgentId(UUID agentId) {
+        this.agentId = agentId;
+    }
+
+    @Override
+    public String getAgentId() {
+        return agentId.toString();
+    }
+
+    private String getAgentQueryKeyFromGlobalAgent() {
+        if (agentId != null) {
+            return agentId.toString();
+        } else {
+            return null;
+        }
+    }
+
+    private String getAgentQueryKeyFromChunkOrGlobalAgent(AgentIdPojo pojo) {
+        String queryKey = getAgentQueryKeyFromGlobalAgent();
+        if (queryKey != null) {
+            return queryKey;
+        } else {
+            return pojo.getAgentId();
+        }
+    }
+
+    @Override
+    public void putPojo(Category cat, boolean replace, AgentIdPojo pojo) {
+        DBCollection coll = getCachedCollection(cat);
+        MongoPojoConverter converter = new MongoPojoConverter();
+        DBObject toInsert = converter.convertPojoToMongo(pojo);
+        String agentId = getAgentQueryKeyFromChunkOrGlobalAgent(pojo);
+        toInsert.put(Key.AGENT_ID.getName(), agentId);
+        if (replace) {
+            // TODO: Split this part out into a separate method. It is a very bad practice to
+            // completely change the behaviour of a method based on a boolean flag.
+            DBObject query = new BasicDBObject();
+            Collection<Key<?>> keys = cat.getKeys();
+            for (Key<?> key : keys) {
+                if (key.isPartialCategoryKey()) {
+                    String name = key.getName();
+                    query.put(name, toInsert.get(name));
+                }
+            }
+            coll.update(query, toInsert, true, false);
+        } else {
+            coll.insert(toInsert);
+        }
+    }
+
+    @Override
+    public void updatePojo(Update update) {
+        assert update instanceof MongoUpdate;
+        MongoUpdate mongoUpdate = (MongoUpdate) update;
+        Category cat = mongoUpdate.getCategory();
+        DBCollection coll = getCachedCollection(cat);
+        DBObject query = mongoUpdate.getQuery();
+        DBObject values = mongoUpdate.getValues();
+        coll.update(query, values);
+    }
+
+    @Override
+    public void removePojo(Remove remove) {
+        assert (remove instanceof MongoRemove);
+        MongoRemove mongoRemove = (MongoRemove) remove;
+        DBObject query = mongoRemove.getQuery();
+        Category category = mongoRemove.getCategory();
+        DBCollection coll = getCachedCollection(category);
+
+        String agentId = getAgentQueryKeyFromGlobalAgent();
+        if (agentId != null) {
+            query.put(Key.AGENT_ID.getName(), agentId);
+        }
+
+        coll.remove(query);
+    }
+
+    private DBCollection getCachedCollection(Category category) {
+        String collName = category.getName();
+        DBCollection coll = collectionCache.get(collName);
+        if (coll == null && db.collectionExists(collName)) {
+            throw new IllegalStateException("Categories need to be registered before being used");
+        }
+        return coll;
+    }
+
+    // TODO: This method is only temporary to enable tests, until we come up with a better design,
+    // in particular, the collection should be stored in the category itself. It must not be called
+    // from production code.
+    void mapCategoryToDBCollection(Category category, DBCollection coll) {
+        collectionCache.put(category.getName(), coll);
+    }
+
+
+    @Override
+    public void purge() {
+        String deleteKey = getAgentQueryKeyFromGlobalAgent();
+        BasicDBObject query = new BasicDBObject(Key.AGENT_ID.getName(), deleteKey);
+        for (DBCollection coll : collectionCache.values()) {
+            coll.remove(query);
+        }
+    }
+    
+    @Override
+    public void registerCategory(Category category) {
+        String name = category.getName();
+        if (collectionCache.containsKey(name)) {
+            throw new IllegalStateException("Category may only be associated with one backend.");
+        }
+
+        DBCollection coll;
+        if (! db.collectionExists(name)) {
+            coll = db.createCollection(name, new BasicDBObject("capped", false));
+        } else {
+            coll = db.getCollection(name);
+        }
+        collectionCache.put(name, coll);
+    }
+
+    @Override
+    public Query createQuery() {
+        return new MongoQuery();
+    }
+
+    @Override
+    public Update createUpdate() {
+        return new MongoUpdate();
+    }
+
+    @Override
+    public Remove createRemove() {
+        return new MongoRemove();
+    }
+
+    @Override
+    public <T extends Pojo> Cursor<T> findAllPojos(Query query, Class<T> resultClass) {
+        MongoQuery mongoQuery =  checkAndCastQuery(query);
+        DBCollection coll = getCachedCollection(mongoQuery.getCategory());
+        DBCursor dbCursor;
+        if (mongoQuery.hasClauses()) {
+            dbCursor = coll.find(mongoQuery.getGeneratedQuery());
+        } else {
+            dbCursor = coll.find();
+        }
+        dbCursor = applySortAndLimit(mongoQuery, dbCursor);
+        return new MongoCursor<T>(dbCursor, resultClass);
+    }
+
+    private DBCursor applySortAndLimit(MongoQuery query, DBCursor dbCursor) {
+        BasicDBObject orderBy = new BasicDBObject();
+        List<Sort> sorts = query.getSorts();
+        for (Sort sort : sorts) {
+            orderBy.append(sort.getKey().getName(), sort.getDirection().getValue());
+        }
+        dbCursor.sort(orderBy);
+        int limit = query.getLimit();
+        if (limit > 0) {
+            dbCursor.limit(limit);
+        }
+        return dbCursor;
+    }
+
+
+    @Override
+    public <T extends Pojo> T findPojo(Query query, Class<T> resultClass) {
+        MongoQuery mongoQuery = checkAndCastQuery(query);
+        DBCollection coll = getCachedCollection(mongoQuery.getCategory());
+        DBObject dbResult = coll.findOne(mongoQuery.getGeneratedQuery());
+        MongoPojoConverter conv = new MongoPojoConverter();
+        return conv.convertMongoToPojo(dbResult, resultClass);
+    }
+
+    private MongoQuery checkAndCastQuery(Query query) {
+        if (!(query instanceof MongoQuery)) {
+            throw new IllegalArgumentException("MongoStorage can only handle MongoQuery");
+        }
+
+        return (MongoQuery) query;
+
+    }
+
+    @Override
+    public long getCount(Category category) {
+        DBCollection coll = getCachedCollection(category);
+        if (coll != null) {
+            return coll.getCount();
+        }
+        return 0L;
+    }
+
+    @Override
+    public void saveFile(String filename, InputStream data) {
+        GridFS gridFS = new GridFS(db);
+        GridFSInputFile inputFile = gridFS.createFile(data, filename);
+        inputFile.save();
+    }
+
+    @Override
+    public InputStream loadFile(String filename) {
+        GridFS gridFS = new GridFS(db);
+        GridFSDBFile file = gridFS.findOne(filename);
+        if (file == null) {
+            return null;
+        } else {
+            return file.getInputStream();
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoStorageProvider.java	Tue Nov 27 14:29:35 2012 +0100
@@ -0,0 +1,64 @@
+/*
+ * 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.storage.mongodb.internal;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+
+import com.redhat.thermostat.storage.config.StartupConfiguration;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.core.StorageProvider;
+
+public class MongoStorageProvider implements StorageProvider {
+
+    private StartupConfiguration configuration;
+
+    public void setConfig(StartupConfiguration configuration) {
+        this.configuration = configuration;
+    }
+
+    @Override
+    public Storage createStorage() {
+        return new MongoStorage(configuration);
+    }
+
+    @Override
+    public boolean canHandleProtocol() {
+        return configuration.getDBConnectionString().startsWith("mongodb://");
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoUpdate.java	Tue Nov 27 14:29:35 2012 +0100
@@ -0,0 +1,93 @@
+/*
+ * 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.storage.mongodb.internal;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBObject;
+import com.redhat.thermostat.storage.core.Category;
+import com.redhat.thermostat.storage.core.Key;
+import com.redhat.thermostat.storage.core.Update;
+
+// TODO: For now we utilize the Chunk based conversion, and rely on MongoStorage to
+// actually resolve the $set fields. Eventually, we want to convert to DBObject
+// directly, and take advantage of improved semantics of this class.
+class MongoUpdate implements Update {
+
+    private DBObject query;
+    private DBObject values;
+    private Category category;
+
+    @Override
+    public Update from(Category category) {
+        if (query != null || values != null) {
+            throw new IllegalStateException();
+        }
+        this.category = category;
+        return this;
+    }
+
+    Category getCategory() {
+        return category;
+    }
+
+    @Override
+    public <T> Update where(Key<T> key, T value) {
+        if (query == null) {
+            query = new BasicDBObject();
+        }
+        query.put(key.getName(), value);
+        return this;
+    }
+
+    DBObject getQuery() {
+        return query;
+    }
+
+    @Override
+    public <T> Update set(Key<T> key, T value) {
+        if (values == null) {
+            values = new BasicDBObject();
+        }
+        values.put(key.getName(), value);
+        return this;
+    }
+
+    DBObject getValues() {
+        return new BasicDBObject("$set", values);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/mongo/src/main/resources/META-INF/p2.inf	Tue Nov 27 14:29:35 2012 +0100
@@ -0,0 +1,7 @@
+# Instructs Eclipse update manager to auto-start this bundle
+instructions.configure = \
+org.eclipse.equinox.p2.touchpoint.eclipse.setStartLevel(startLevel: 4); \
+org.eclipse.equinox.p2.touchpoint.eclipse.markStarted(started: true);
+instructions.unconfigure = \
+org.eclipse.equinox.p2.touchpoint.eclipse.setStartLevel(startLevel: -1); \
+org.eclipse.equinox.p2.touchpoint.eclipse.markStarted(started: false);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/mongo/src/test/java/com/redhat/thermostat/storage/mongodb/internal/MongoConnectionTest.java	Tue Nov 27 14:29:35 2012 +0100
@@ -0,0 +1,127 @@
+/*
+ * 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.storage.mongodb.internal;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+
+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.storage.config.StartupConfiguration;
+import com.redhat.thermostat.storage.core.ConnectionException;
+import com.redhat.thermostat.storage.core.StorageConstants;
+import com.redhat.thermostat.storage.core.Connection.ConnectionListener;
+import com.redhat.thermostat.storage.core.Connection.ConnectionStatus;
+import com.redhat.thermostat.storage.mongodb.internal.MongoConnection;
+
+@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 testConnectIOException() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenThrow(new IOException());
+        boolean exceptionThrown = false;
+        try {
+            conn.connect();
+        } catch (ConnectionException ex) {
+            exceptionThrown = true;
+        }
+        verify(listener).changed(ConnectionStatus.FAILED_TO_CONNECT);
+        assertTrue(exceptionThrown);
+    }
+
+    @Test
+    public void testConnectMongoException() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenThrow(new MongoException("fluff"));
+        boolean exceptionThrown = false;
+        try {
+            conn.connect();
+        } catch (ConnectionException ex) {
+            exceptionThrown = true;
+        }
+
+        verify(listener).changed(ConnectionStatus.FAILED_TO_CONNECT);
+        assertTrue(exceptionThrown);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/mongo/src/test/java/com/redhat/thermostat/storage/mongodb/internal/MongoCursorTest.java	Tue Nov 27 14:29:35 2012 +0100
@@ -0,0 +1,157 @@
+/*
+ * 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.storage.mongodb.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBCursor;
+import com.mongodb.DBObject;
+import com.redhat.thermostat.storage.core.Category;
+import com.redhat.thermostat.storage.core.Cursor;
+import com.redhat.thermostat.storage.core.Entity;
+import com.redhat.thermostat.storage.core.Key;
+import com.redhat.thermostat.storage.core.Persist;
+import com.redhat.thermostat.storage.model.BasePojo;
+import com.redhat.thermostat.storage.mongodb.internal.MongoCursor;
+
+public class MongoCursorTest {
+
+    @Entity
+    public static class TestClass extends BasePojo {
+        private String key1;
+        private String key2;
+        private String key3;
+        private String key4;
+        @Persist
+        public String getKey1() {
+            return key1;
+        }
+        @Persist
+        public void setKey1(String key1) {
+            this.key1 = key1;
+        }
+        @Persist
+        public String getKey2() {
+            return key2;
+        }
+        @Persist
+        public void setKey2(String key2) {
+            this.key2 = key2;
+        }
+        @Persist
+        public String getKey3() {
+            return key3;
+        }
+        @Persist
+        public void setKey3(String key3) {
+            this.key3 = key3;
+        }
+        @Persist
+        public String getKey4() {
+            return key4;
+        }
+        @Persist
+        public void setKey4(String key4) {
+            this.key4 = key4;
+        }
+    }
+
+    private static final Key<String> key1 = new Key<>("key1", false);
+    private static final Key<String> key2 = new Key<>("key2", false);
+    private static final Key<String> key3 = new Key<>("key3", false);
+    private static final Key<String> key4 = new Key<>("key4", false);
+
+    private static final Category testCategory = new Category("MongoCursorTest", key1, key2, key3, key4);
+
+    private DBCursor dbCursor;
+    private Cursor<TestClass> cursor;
+
+    @Before
+    public void setUp() {
+        
+        BasicDBObject value1 = new BasicDBObject();
+        value1.put("key1", "test1");
+        value1.put("key2", "test2");
+        BasicDBObject value2 = new BasicDBObject();
+        value2.put("key3", "test3");
+        value2.put("key4", "test4");
+
+        dbCursor = mock(DBCursor.class);
+        when(dbCursor.hasNext()).thenReturn(true).thenReturn(true).thenReturn(false);
+        when(dbCursor.next()).thenReturn(value1).thenReturn(value2).thenReturn(null);
+        when(dbCursor.sort(any(DBObject.class))).thenReturn(dbCursor);
+        when(dbCursor.limit(anyInt())).thenReturn(dbCursor);
+        cursor = new MongoCursor<TestClass>(dbCursor, TestClass.class);
+
+    }
+
+    @After
+    public void tearDown() {
+        dbCursor = null;
+        cursor = null;
+    }
+
+    @Test
+    public void verifySimpleCursor() {
+
+        assertTrue(cursor.hasNext());
+        TestClass obj1 = cursor.next();
+        assertEquals("test1", obj1.getKey1());
+        assertEquals("test2", obj1.getKey2());
+
+        assertTrue(cursor.hasNext());
+        TestClass obj2 = cursor.next();
+        assertEquals("test3", obj2.getKey3());
+        assertEquals("test4", obj2.getKey4());
+
+        assertFalse(cursor.hasNext());
+        assertNull(cursor.next());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/mongo/src/test/java/com/redhat/thermostat/storage/mongodb/internal/MongoPojoConverterTest.java	Tue Nov 27 14:29:35 2012 +0100
@@ -0,0 +1,327 @@
+/*
+ * 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.storage.mongodb.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBObject;
+import com.redhat.thermostat.storage.core.Entity;
+import com.redhat.thermostat.storage.core.Persist;
+import com.redhat.thermostat.storage.core.StorageException;
+import com.redhat.thermostat.storage.model.Pojo;
+import com.redhat.thermostat.storage.mongodb.internal.MongoPojoConverter;
+
+public class MongoPojoConverterTest {
+
+    @Entity
+    public static class SimplePojo implements Pojo {
+        
+        private String test;
+        private String ignored;
+
+
+        @Persist
+        public String getTest() {
+            return test;
+        }
+
+        @Persist
+        public void setTest(String test) {
+            this.test = test;
+        }
+
+        public String getIgnored() {
+            return ignored;
+        }
+
+        public void setIgnored(String ignored) {
+            this.ignored = ignored;
+        }
+        
+    }
+
+    @Entity
+    public static class NestedPojo extends SimplePojo {
+
+        private SimplePojo nested;
+
+        @Persist
+        public SimplePojo getNested() {
+            return nested;
+        }
+
+        @Persist
+        public void setNested(SimplePojo nested) {
+            this.nested = nested;
+        }
+    }
+
+    @Entity
+    public static class IndexedPojo extends SimplePojo {
+
+        private SimplePojo[] indexed;
+
+        @Persist
+        public SimplePojo[] getIndexed() {
+            return indexed;
+        }
+
+        @Persist
+        public void setIndexed(SimplePojo[] indexed) {
+            this.indexed = indexed;
+        }
+    }
+
+    @Entity
+    public static class PrimitiveIndexedPojo extends SimplePojo {
+
+        private int[] indexed;
+
+        @Persist
+        public int[] getIndexed() {
+            return indexed;
+        }
+
+        @Persist
+        public void setIndexed(int[] indexed) {
+            this.indexed = indexed;
+        }
+    }
+
+    public static class BrokenPojo1 extends SimplePojo {
+        private int broken;
+
+        @Persist
+        public void setBroken(int broken) {
+            this.broken = broken;
+        }
+    }
+
+    public static class BrokenPojo2 extends SimplePojo {
+        private int broken;
+
+        @Persist
+        public void setBroken(int broken) {
+            this.broken = broken;
+        }
+
+        public int getBroken() {
+            return broken;
+        }
+    }
+
+    @Test
+    public void testConvertSimplePojoToMongo() {
+        MongoPojoConverter conv = new MongoPojoConverter();
+        SimplePojo obj = new SimplePojo();
+        obj.setTest("fluff");
+        DBObject dbObject = conv.convertPojoToMongo(obj);
+        assertEquals(1, dbObject.keySet().size());
+        assertEquals("fluff", dbObject.get("test"));
+    }
+
+    @Test
+    public void testConvertSimplePojoFromMongo() {
+        MongoPojoConverter conv = new MongoPojoConverter();
+        DBObject dbObj = new BasicDBObject();
+        dbObj.put("test", "fluff");
+        SimplePojo obj = conv.convertMongoToPojo(dbObj, SimplePojo.class);
+        assertEquals("fluff", obj.getTest());
+        assertNull(obj.getIgnored());
+    }
+
+    @Test(expected=StorageException.class)
+    public void testConvertSimplePojoFromMongoExtraProperty() {
+        MongoPojoConverter conv = new MongoPojoConverter();
+        DBObject dbObj = new BasicDBObject();
+        dbObj.put("test", "fluff");
+        dbObj.put("foo", "bar");
+        conv.convertMongoToPojo(dbObj, SimplePojo.class);
+    }
+
+    @Test(expected=StorageException.class)
+    public void testConvertSimplePojoFromMongoBrokenPojo1() {
+        MongoPojoConverter conv = new MongoPojoConverter();
+        DBObject dbObj = new BasicDBObject();
+        dbObj.put("broken", "foo");
+        conv.convertMongoToPojo(dbObj, BrokenPojo1.class);
+    }
+
+
+    @Test(expected=StorageException.class)
+    public void testConvertSimplePojoFromMongoBrokenPojo2() {
+        MongoPojoConverter conv = new MongoPojoConverter();
+        DBObject dbObj = new BasicDBObject();
+        dbObj.put("broken", "foo");
+        conv.convertMongoToPojo(dbObj, BrokenPojo2.class);
+    }
+
+    @Test
+    public void testConvertNestedPojoToMongo() {
+        MongoPojoConverter conv = new MongoPojoConverter();
+        NestedPojo obj = new NestedPojo();
+        obj.setTest("foo");
+        SimplePojo nested = new SimplePojo();
+        nested.setTest("bar");
+        obj.setNested(nested);
+        DBObject dbObject = conv.convertPojoToMongo(obj);
+        assertEquals(2, dbObject.keySet().size());
+        assertEquals("foo", dbObject.get("test"));
+        DBObject nestedDbObj = (DBObject) dbObject.get("nested");
+        assertEquals(1, nestedDbObj.keySet().size());
+        assertEquals("bar", nestedDbObj.get("test"));
+    }
+
+    @Test
+    public void testConvertNestedPojoFromMongo() {
+        MongoPojoConverter conv = new MongoPojoConverter();
+        DBObject nested = new BasicDBObject();
+        nested.put("test", "bar");
+        DBObject dbObj = new BasicDBObject();
+        dbObj.put("test", "foo");
+        dbObj.put("nested", nested);
+        NestedPojo obj = conv.convertMongoToPojo(dbObj, NestedPojo.class);
+        assertEquals("foo", obj.getTest());
+        assertNull(obj.getIgnored());
+        assertNotNull(obj.getNested());
+        assertEquals("bar", obj.getNested().getTest());
+        assertEquals(null, obj.getNested().getIgnored());
+    }
+
+    @Test
+    public void testConvertIndexedPojoToMongo() {
+        MongoPojoConverter conv = new MongoPojoConverter();
+        IndexedPojo obj = new IndexedPojo();
+        obj.setTest("test");
+        SimplePojo obj1 = new SimplePojo();
+        obj1.setTest("test1");
+        SimplePojo obj2 = new SimplePojo();
+        obj2.setTest("test2");
+        SimplePojo obj3 = new SimplePojo();
+        obj3.setTest("test3");
+        obj.setIndexed(new SimplePojo[] { obj1, obj2, obj3 });
+
+        DBObject dbObject = conv.convertPojoToMongo(obj);
+        assertEquals(2, dbObject.keySet().size());
+        assertEquals("test", dbObject.get("test"));
+        List<?> indexedDbObj = (List<?>) dbObject.get("indexed");
+        assertEquals(3, indexedDbObj.size());
+
+        DBObject dbObj1 = (DBObject) indexedDbObj.get(0);
+        assertEquals(1, dbObj1.keySet().size());
+        assertEquals("test1", dbObj1.get("test"));
+
+        DBObject dbObj2 = (DBObject) indexedDbObj.get(1);
+        assertEquals(1, dbObj2.keySet().size());
+        assertEquals("test2", dbObj2.get("test"));
+
+        DBObject dbObj3 = (DBObject) indexedDbObj.get(2);
+        assertEquals(1, dbObj3.keySet().size());
+        assertEquals("test3", dbObj3.get("test"));
+    }
+
+    @Test
+    public void testConvertPrimitiveIndexedPojoToMongo() {
+        MongoPojoConverter conv = new MongoPojoConverter();
+        PrimitiveIndexedPojo obj = new PrimitiveIndexedPojo();
+        obj.setTest("test");
+        obj.setIndexed(new int[] { 1, 2, 3 });
+
+        DBObject dbObject = conv.convertPojoToMongo(obj);
+        assertEquals(2, dbObject.keySet().size());
+        assertEquals("test", dbObject.get("test"));
+        List<?> indexedDbObj = (List<?>) dbObject.get("indexed");
+        assertEquals(3, indexedDbObj.size());
+
+        assertEquals(1, indexedDbObj.get(0));
+        assertEquals(2, indexedDbObj.get(1));
+        assertEquals(3, indexedDbObj.get(2));
+
+    }
+
+    @Test
+    public void testConvertIndexedPojoFromMongo() {
+        MongoPojoConverter conv = new MongoPojoConverter();
+        DBObject indexed = new BasicDBObject();
+        indexed.put("test", "test");
+        DBObject dbObj1 = new BasicDBObject();
+        dbObj1.put("test", "test1");
+        DBObject dbObj2 = new BasicDBObject();
+        dbObj2.put("test", "test2");
+        DBObject dbObj3 = new BasicDBObject();
+        dbObj3.put("test", "test3");
+        indexed.put("indexed", Arrays.asList(dbObj1, dbObj2, dbObj3));
+
+        IndexedPojo obj = conv.convertMongoToPojo(indexed, IndexedPojo.class);
+        assertEquals("test", obj.getTest());
+        assertNull(obj.getIgnored());
+        assertNotNull(obj.getIndexed());
+        SimplePojo[] indexedObj = obj.getIndexed();
+        assertEquals("test1", indexedObj[0].getTest());
+        assertNull(indexedObj[0].getIgnored());
+        assertEquals("test2", indexedObj[1].getTest());
+        assertNull(indexedObj[1].getIgnored());
+        assertEquals("test3", indexedObj[2].getTest());
+        assertNull(indexedObj[2].getIgnored());
+    }
+
+    @Test
+    public void testConvertPrimitiveIndexedPojoFromMongo() {
+        MongoPojoConverter conv = new MongoPojoConverter();
+        DBObject indexed = new BasicDBObject();
+        indexed.put("test", "test");
+        indexed.put("indexed", Arrays.asList(1, 2, 3));
+
+        PrimitiveIndexedPojo obj = conv.convertMongoToPojo(indexed, PrimitiveIndexedPojo.class);
+        assertEquals("test", obj.getTest());
+        assertNull(obj.getIgnored());
+        assertNotNull(obj.getIndexed());
+        int[] indexedObj = obj.getIndexed();
+        assertEquals(1, indexedObj[0]);
+        assertEquals(2, indexedObj[1]);
+        assertEquals(3, indexedObj[2]);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/mongo/src/test/java/com/redhat/thermostat/storage/mongodb/internal/MongoQueryTest.java	Tue Nov 27 14:29:35 2012 +0100
@@ -0,0 +1,107 @@
+/*
+ * 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.storage.mongodb.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBObject;
+import com.redhat.thermostat.storage.core.Category;
+import com.redhat.thermostat.storage.core.Query;
+import com.redhat.thermostat.storage.core.Query.Criteria;
+import com.redhat.thermostat.storage.mongodb.internal.MongoQuery;
+
+public class MongoQueryTest {
+
+    @Test
+    public void testEmptyQuery() {
+        MongoQuery query = new MongoQuery();
+        DBObject mongoQuery = query.getGeneratedQuery();
+        assertTrue(mongoQuery.keySet().isEmpty());
+    }
+
+    @Test
+    public void testCollectionName() {
+        MongoQuery query = new MongoQuery().from(new Category("some-collection"));
+        assertEquals("some-collection", query.getCategory().getName());
+    }
+
+    @Test
+    public void testWhereEquals() {
+        DBObject generatedQuery = generateSimpleWhereQuery("key", Criteria.EQUALS, "value");
+        assertEquals("value", generatedQuery.get("key"));
+    }
+
+    @Test
+    public void testWhereNotEquals() {
+        DBObject generatedQuery = generateSimpleWhereQuery("key", Criteria.NOT_EQUAL_TO, "value");
+        assertEquals(new BasicDBObject("$ne", "value"), generatedQuery.get("key"));
+    }
+
+    @Test
+    public void testWhereGreaterThan() {
+        DBObject generatedQuery = generateSimpleWhereQuery("key", Criteria.GREATER_THAN, "value");
+        assertEquals(new BasicDBObject("$gt", "value"), generatedQuery.get("key"));
+    }
+
+    @Test
+    public void testWhereGreaterThanOrEqualTo() {
+        DBObject generatedQuery = generateSimpleWhereQuery("key", Criteria.GREATER_THAN_OR_EQUAL_TO, "value");
+        assertEquals(new BasicDBObject("$gte", "value"), generatedQuery.get("key"));
+    }
+
+    @Test
+    public void testWhereLessThan() {
+        DBObject generatedQuery = generateSimpleWhereQuery("key", Criteria.LESS_THAN, "value");
+        assertEquals(new BasicDBObject("$lt", "value"), generatedQuery.get("key"));
+    }
+
+    @Test
+    public void testWhereLessThanOrEqualTo() {
+        DBObject generatedQuery = generateSimpleWhereQuery("key", Criteria.LESS_THAN_OR_EQUAL_TO, "value");
+        assertEquals(new BasicDBObject("$lte", "value"), generatedQuery.get("key"));
+    }
+
+    private DBObject generateSimpleWhereQuery(String key, Criteria criteria, Object value) {
+        MongoQuery query = new MongoQuery().where(key, criteria, value);
+        return query.getGeneratedQuery();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/mongo/src/test/java/com/redhat/thermostat/storage/mongodb/internal/MongoStorageTest.java	Tue Nov 27 14:29:35 2012 +0100
@@ -0,0 +1,527 @@
+/*
+ * 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.storage.mongodb.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.UUID;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DB;
+import com.mongodb.DBCollection;
+import com.mongodb.DBCursor;
+import com.mongodb.DBObject;
+import com.mongodb.Mongo;
+import com.mongodb.MongoURI;
+import com.mongodb.gridfs.GridFS;
+import com.mongodb.gridfs.GridFSDBFile;
+import com.mongodb.gridfs.GridFSInputFile;
+import com.redhat.thermostat.storage.config.StartupConfiguration;
+import com.redhat.thermostat.storage.core.Category;
+import com.redhat.thermostat.storage.core.Cursor;
+import com.redhat.thermostat.storage.core.Entity;
+import com.redhat.thermostat.storage.core.Key;
+import com.redhat.thermostat.storage.core.Persist;
+import com.redhat.thermostat.storage.core.Query;
+import com.redhat.thermostat.storage.core.Update;
+import com.redhat.thermostat.storage.core.Query.Criteria;
+import com.redhat.thermostat.storage.core.Query.SortDirection;
+import com.redhat.thermostat.storage.model.BasePojo;
+import com.redhat.thermostat.storage.mongodb.internal.MongoConnection;
+import com.redhat.thermostat.storage.mongodb.internal.MongoQuery;
+import com.redhat.thermostat.storage.mongodb.internal.MongoStorage;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({ DBCollection.class, DB.class, Mongo.class, MongoStorage.class, MongoConnection.class })
+public class MongoStorageTest {
+
+    @Entity
+    public static class TestClass extends BasePojo {
+        private String key1;
+        private String key2;
+        private String key3;
+        private String key4;
+        private String key5;
+        @Persist
+        public String getKey1() {
+            return key1;
+        }
+        @Persist
+        public void setKey1(String key1) {
+            this.key1 = key1;
+        }
+        @Persist
+        public String getKey2() {
+            return key2;
+        }
+        @Persist
+        public void setKey2(String key2) {
+            this.key2 = key2;
+        }
+        @Persist
+        public String getKey3() {
+            return key3;
+        }
+        @Persist
+        public void setKey3(String key3) {
+            this.key3 = key3;
+        }
+        @Persist
+        public String getKey4() {
+            return key4;
+        }
+        @Persist
+        public void setKey4(String key4) {
+            this.key4 = key4;
+        }
+        @Persist
+        public String getKey5() {
+            return key5;
+        }
+        @Persist
+        public void setKey5(String key5) {
+            this.key5 = key5;
+        }
+    }
+
+    private static final Key<String> key1 = new Key<>("key1", true);
+    private static final Key<String> key2 = new Key<>("key2", true);
+    private static final Key<String> key3 = new Key<>("key3", false);
+    private static final Key<String> key4 = new Key<>("key4", false);
+    private static final Key<String> key5 = new Key<>("key5", false);
+    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 Mongo m;
+    private DB db;
+    private DBCollection testCollection, emptyTestCollection, mockedCollection;
+    private DBCursor cursor;
+
+    private MongoStorage makeStorage() {
+        MongoStorage storage = new MongoStorage(conf);
+        storage.mapCategoryToDBCollection(testCategory, testCollection);
+        storage.mapCategoryToDBCollection(emptyTestCategory, emptyTestCollection);
+        return storage;
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        conf = mock(StartupConfiguration.class);
+        when(conf.getDBConnectionString()).thenReturn("mongodb://127.0.0.1:27518");
+        db = PowerMockito.mock(DB.class);
+        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");
+        value1.put("key2", "test2");
+        BasicDBObject value2 = new BasicDBObject();
+        value2.put("key3", "test3");
+        value2.put("key4", "test4");
+
+        cursor = mock(DBCursor.class);
+        when(cursor.hasNext()).thenReturn(true).thenReturn(true).thenReturn(false);
+        when(cursor.next()).thenReturn(value1).thenReturn(value2).thenReturn(null);
+
+        testCollection = PowerMockito.mock(DBCollection.class);
+        when(testCollection.find(any(DBObject.class))).thenReturn(cursor);
+        when(testCollection.find()).thenReturn(cursor);
+        when(testCollection.findOne(any(DBObject.class))).thenReturn(value1);
+        when(testCollection.getCount()).thenReturn(2L);
+        emptyTestCollection = PowerMockito.mock(DBCollection.class);
+        when(emptyTestCollection.getCount()).thenReturn(0L);
+        when(db.collectionExists(anyString())).thenReturn(false);
+        when(db.createCollection(anyString(), any(DBObject.class))).thenReturn(testCollection);
+    }
+
+    @After
+    public void tearDown() {
+        conf = null;
+        m = null;
+        db = null;
+        testCollection = null;
+        emptyTestCollection = null;
+        cursor = null;
+    }
+
+    @Test (expected=IllegalArgumentException.class)
+    public void verifyFindOnlyAcceptsMongoQuery() {
+        MongoStorage storage = makeStorage();
+        Query query = mock(Query.class);
+        storage.findPojo(query, TestClass.class);
+    }
+
+    @Test (expected=IllegalArgumentException.class)
+    public void verifyFindAllOnlyAcceptsMongoQuery() {
+        MongoStorage storage = makeStorage();
+        Query query = mock(Query.class);
+        storage.findAllPojos(query, TestClass.class);
+    }
+
+    @Test
+    public void verifyFindAllReturnsCursor() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
+        Query query = storage.createQuery().from(testCategory);
+        Cursor<TestClass> cursor = storage.findAllPojos(query, TestClass.class);
+        assertNotNull(cursor);
+    }
+
+    @Test
+    public void verifyFindReturnsChunk() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
+        Query query = storage.createQuery().from(testCategory).where(key1, Criteria.EQUALS, "test1");
+        TestClass result = storage.findPojo(query, TestClass.class);
+        assertNotNull(result);
+    }
+
+    @Test
+    public void verifyFindAllCallsDBCollectionFind() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
+        Query query = storage.createQuery().from(testCategory).where(key1, Criteria.EQUALS, "fluff");
+        storage.findAllPojos(query, TestClass.class);
+        verify(testCollection).find(any(DBObject.class));
+    }
+
+    @Test
+    public void verifyFindCallsDBCollectionFindOne() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
+        Query query = storage.createQuery().from(testCategory);
+        storage.findPojo(query, TestClass.class);
+        verify(testCollection).findOne(any(DBObject.class));
+    }
+
+    @Test
+    public void verifyFindAllCallsDBCollectionFindWithCorrectQuery() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
+
+        MongoQuery query = mock(MongoQuery.class);
+        when(query.hasClauses()).thenReturn(true);
+        DBObject generatedQuery = mock(DBObject.class);
+        when(query.getGeneratedQuery()).thenReturn(generatedQuery);
+        when(query.getCategory()).thenReturn(testCategory);
+        ArgumentCaptor<DBObject> findArg = ArgumentCaptor.forClass(DBObject.class);
+
+        storage.findAllPojos(query, TestClass.class);
+
+        verify(testCollection).find(findArg.capture());
+        assertSame(generatedQuery, findArg.getValue());
+    }
+
+    @Test
+    public void verifyFindCallsDBCollectionFindOneWithCorrectQuery() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
+
+        MongoQuery query = mock(MongoQuery.class);
+        DBObject generatedQuery = mock(DBObject.class);
+        when(query.getGeneratedQuery()).thenReturn(generatedQuery);
+        when(query.getCategory()).thenReturn(testCategory);
+
+        ArgumentCaptor<DBObject> findArg = ArgumentCaptor.forClass(DBObject.class);
+
+        storage.findPojo(query, TestClass.class);
+
+        verify(testCollection).findOne(findArg.capture());
+        assertSame(generatedQuery, findArg.getValue());
+    }
+
+    @Test
+    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
+        // Because we mock the DBCollection, the contents of this query don't actually determine the result.
+        MongoQuery query = new MongoQuery().from(testCategory);
+
+        TestClass result = storage.findPojo(query, TestClass.class);
+
+        assertNotNull(result);
+        assertEquals("test1", result.getKey1());
+        assertEquals("test2", result.getKey2());
+    }
+
+    @Test
+    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
+        // Because we mock the DBCollection, the contents of this query don't actually determine the result.
+        MongoQuery query = new MongoQuery().from(testCategory);
+
+        Cursor<TestClass> cursor = storage.findAllPojos(query, TestClass.class);
+
+        verifyDefaultCursor(cursor);
+    }
+
+    @Test
+    public void verifyFindAllWithSortAndLimit() 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
+        // Because we mock the DBCollection, the contents of this query don't actually determine the result.
+        MongoQuery query = (MongoQuery) new MongoQuery().from(testCategory).sort(key1, Query.SortDirection.ASCENDING).limit(3);
+
+        Cursor<TestClass> cursor = storage.findAllPojos(query, TestClass.class);
+
+        verifyDefaultCursor(cursor);
+        ArgumentCaptor<DBObject> orderBy = ArgumentCaptor.forClass(DBObject.class);
+        verify(this.cursor).sort(orderBy.capture());
+        assertTrue(orderBy.getValue().containsField("key1"));
+        assertEquals(1, orderBy.getValue().get("key1"));
+        verify(this.cursor).limit(3);
+    }
+
+    @Test
+    public void verifyFindAllFromCategoryCallsDBCollectionFindAll() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
+        Query query = storage.createQuery().from(testCategory);
+        storage.findAllPojos(query, TestClass.class);
+        verify(testCollection).find();
+    }
+
+    @Test
+    public void verifyFindAllFromCategoryReturnsCorrectCursor() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
+        Query query = storage.createQuery().from(testCategory);
+        Cursor<TestClass> cursor = storage.findAllPojos(query, TestClass.class);
+
+        verifyDefaultCursor(cursor);
+    }
+
+    @Test
+    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() 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() 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);
+    }
+
+    private void verifyDefaultCursor(Cursor<TestClass> cursor) {
+        assertTrue(cursor.hasNext());
+        TestClass obj1 = cursor.next();
+        assertEquals("test1", obj1.getKey1());
+        assertEquals("test2", obj1.getKey2());
+
+        assertTrue(cursor.hasNext());
+        TestClass obj2 = cursor.next();
+        assertEquals("test3", obj2.getKey3());
+        assertEquals("test4", obj2.getKey4());
+
+        assertFalse(cursor.hasNext());
+        assertNull(cursor.next());
+    }
+
+    @Test
+    public void verifySaveFile() throws Exception {
+        GridFSInputFile gridFSFile = mock(GridFSInputFile.class);
+        GridFS gridFS = mock(GridFS.class);
+        when(gridFS.createFile(any(InputStream.class), anyString())).thenReturn(gridFSFile);
+        PowerMockito.whenNew(GridFS.class).withArguments(any()).thenReturn(gridFS);
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
+        byte[] data = new byte[] { 1, 2, 3 };
+        InputStream dataStream = new ByteArrayInputStream(data);
+        storage.saveFile("test", dataStream);
+        verify(gridFS).createFile(same(dataStream), eq("test"));
+        verify(gridFSFile).save();
+    }
+
+    @Test
+    public void verifyPutChunkUsesCorrectChunkAgent() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
+        TestClass pojo = new TestClass();
+        pojo.setAgentId("123");
+        storage.putPojo(testCategory, false, pojo);
+        ArgumentCaptor<DBObject> dbobj = ArgumentCaptor.forClass(DBObject.class);
+        verify(testCollection).insert(dbobj.capture());
+        DBObject val = dbobj.getValue();
+        assertEquals("123", val.get("agentId"));
+    }
+
+    @Test
+    public void verifyPutChunkUsesCorrectGlobalAgent() throws Exception {
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
+        storage.setAgentId(new UUID(1, 2));
+        TestClass pojo = new TestClass();
+        pojo.setAgentId("123");
+        storage.putPojo(testCategory, false, pojo);
+        ArgumentCaptor<DBObject> dbobj = ArgumentCaptor.forClass(DBObject.class);
+        verify(testCollection).insert(dbobj.capture());
+        DBObject val = dbobj.getValue();
+        assertEquals(new UUID(1, 2).toString(), val.get("agentId"));
+    }
+
+    @Test
+    public void verifyLoadFile() throws Exception {
+        InputStream stream = mock(InputStream.class);
+        GridFSDBFile file = mock(GridFSDBFile.class);
+        when(file.getInputStream()).thenReturn(stream);
+        GridFS gridFS = mock(GridFS.class);
+        when(gridFS.findOne("test")).thenReturn(file);
+        PowerMockito.whenNew(GridFS.class).withArguments(any()).thenReturn(gridFS);
+        PowerMockito.whenNew(Mongo.class).withParameterTypes(MongoURI.class).withArguments(any(MongoURI.class)).thenReturn(m);
+        MongoStorage storage = makeStorage();
+
+        InputStream actual = storage.loadFile("test");
+        assertSame(stream, actual);
+
+        actual = storage.loadFile("doesnotexist");
+        assertNull(actual);
+    }
+
+    @Test
+    public void verifySimpleUpdate() {
+        MongoStorage storage = makeStorage();
+        Update update = storage.createUpdate().from(testCategory).where(Key.AGENT_ID, "test1").set(key2, "test2");
+        storage.updatePojo(update);
+
+        ArgumentCaptor<DBObject> queryCaptor = ArgumentCaptor.forClass(DBObject.class);
+        ArgumentCaptor<DBObject> valueCaptor = ArgumentCaptor.forClass(DBObject.class);
+        
+        verify(testCollection).update(queryCaptor.capture(), valueCaptor.capture());
+        DBObject query = queryCaptor.getValue();
+        assertTrue(query.containsField(Key.AGENT_ID.getName()));
+        assertEquals("test1", query.get(Key.AGENT_ID.getName()));
+    }
+
+    @Test
+    public void verifyMultiFieldUpdate() {
+        MongoStorage storage = makeStorage();
+        Update update = storage.createUpdate().from(testCategory).where(Key.AGENT_ID, "test1").set(key2, "test2").set(key3, "test3");
+        storage.updatePojo(update);
+
+        ArgumentCaptor<DBObject> queryCaptor = ArgumentCaptor.forClass(DBObject.class);
+        ArgumentCaptor<DBObject> valueCaptor = ArgumentCaptor.forClass(DBObject.class);
+        
+        verify(testCollection).update(queryCaptor.capture(), valueCaptor.capture());
+        DBObject query = queryCaptor.getValue();
+        assertTrue(query.containsField(Key.AGENT_ID.getName()));
+        assertEquals("test1", query.get(Key.AGENT_ID.getName()));
+        DBObject value = valueCaptor.getValue();
+        assertTrue(value.containsField("$set"));
+        DBObject values = (DBObject) value.get("$set");
+        assertTrue(values.containsField("key2"));
+        assertEquals("test2", values.get("key2"));
+        assertTrue(values.containsField("key3"));
+        assertEquals("test3", values.get("key3"));
+    }
+
+    @Test
+    public void verifyInsertReplaceCallsUpdate() {
+        TestClass pojo = new TestClass();
+        pojo.setAgentId("123");
+        pojo.setKey1("test1");
+        pojo.setKey2("test2");
+        pojo.setKey3("test3");
+        pojo.setKey4("test4");
+        pojo.setKey5("test5");
+
+        MongoStorage storage = makeStorage();
+        storage.putPojo(testCategory, true, pojo);
+
+        ArgumentCaptor<DBObject> queryCaptor = ArgumentCaptor.forClass(DBObject.class);
+        ArgumentCaptor<DBObject> valueCaptor = ArgumentCaptor.forClass(DBObject.class);
+        verify(testCollection).update(queryCaptor.capture(), valueCaptor.capture(), eq(true), eq(false));
+
+        DBObject query = queryCaptor.getValue();
+        assertEquals(2, query.keySet().size());
+        assertEquals("test1", query.get("key1"));
+        assertEquals("test2", query.get("key2"));
+
+        DBObject value = valueCaptor.getValue();
+        assertEquals(6, value.keySet().size());
+        assertEquals("test1", value.get("key1"));
+        assertEquals("test2", value.get("key2"));
+        assertEquals("test3", value.get("key3"));
+        assertEquals("test4", value.get("key4"));
+        assertEquals("test5", value.get("key5"));
+        assertEquals("123", value.get("agentId"));
+    }
+}
--- a/storage/pom.xml	Wed Nov 28 09:04:08 2012 -0500
+++ b/storage/pom.xml	Tue Nov 27 14:29:35 2012 +0100
@@ -60,6 +60,7 @@
 
   <modules>
     <module>core</module>
+    <module>mongo</module>
   </modules>
 
 </project>
--- a/web/client/src/main/java/com/redhat/thermostat/web/client/WebStorageProvider.java	Wed Nov 28 09:04:08 2012 -0500
+++ b/web/client/src/main/java/com/redhat/thermostat/web/client/WebStorageProvider.java	Tue Nov 27 14:29:35 2012 +0100
@@ -1,6 +1,6 @@
 package com.redhat.thermostat.web.client;
 
-import com.redhat.thermostat.common.cli.AuthenticationConfiguration;
+import com.redhat.thermostat.storage.config.AuthenticationConfiguration;
 import com.redhat.thermostat.storage.config.StartupConfiguration;
 import com.redhat.thermostat.storage.core.Storage;
 import com.redhat.thermostat.storage.core.StorageProvider;
--- a/web/cmd/pom.xml	Wed Nov 28 09:04:08 2012 -0500
+++ b/web/cmd/pom.xml	Tue Nov 27 14:29:35 2012 +0100
@@ -91,6 +91,11 @@
       <artifactId>thermostat-web-server</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-storage-mongodb</artifactId>
+      <version>${project.version}</version>
+    </dependency>
 
   </dependencies>
 
--- a/web/cmd/src/main/java/com/redhat/thermostat/web/cmd/WebServiceLauncher.java	Wed Nov 28 09:04:08 2012 -0500
+++ b/web/cmd/src/main/java/com/redhat/thermostat/web/cmd/WebServiceLauncher.java	Tue Nov 27 14:29:35 2012 +0100
@@ -54,7 +54,7 @@
 import org.eclipse.jetty.webapp.WebAppContext;
 
 import com.redhat.thermostat.common.config.InvalidConfigurationException;
-import com.redhat.thermostat.common.storage.MongoStorageProvider;
+import com.redhat.thermostat.storage.mongodb.internal.MongoStorageProvider;
 import com.redhat.thermostat.web.server.IpPortPair;
 import com.redhat.thermostat.web.server.WebStorageEndPoint;
 
--- a/web/common/pom.xml	Wed Nov 28 09:04:08 2012 -0500
+++ b/web/common/pom.xml	Tue Nov 27 14:29:35 2012 +0100
@@ -69,6 +69,11 @@
       <artifactId>thermostat-common-core</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-storage-mongodb</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
 
   <build>
--- a/web/common/src/main/java/com/redhat/thermostat/web/common/StorageWrapper.java	Wed Nov 28 09:04:08 2012 -0500
+++ b/web/common/src/main/java/com/redhat/thermostat/web/common/StorageWrapper.java	Tue Nov 27 14:29:35 2012 +0100
@@ -37,7 +37,7 @@
 
 package com.redhat.thermostat.web.common;
 
-import com.redhat.thermostat.common.storage.MongoStorageProvider;
+import com.redhat.thermostat.storage.mongodb.internal.MongoStorageProvider;
 import com.redhat.thermostat.storage.config.StartupConfiguration;
 import com.redhat.thermostat.storage.core.Storage;
 import com.redhat.thermostat.storage.core.StorageProvider;
--- a/web/war/src/main/webapp/WEB-INF/web.xml	Wed Nov 28 09:04:08 2012 -0500
+++ b/web/war/src/main/webapp/WEB-INF/web.xml	Tue Nov 27 14:29:35 2012 +0100
@@ -9,7 +9,7 @@
   <servlet>
     <init-param>
       <param-name>storage.class</param-name>
-      <param-value>com.redhat.thermostat.common.storage.MongoStorageProvider</param-value>
+      <param-value>com.redhat.thermostat.storage.mongodb.internal.MongoStorageProvider</param-value>
     </init-param>
     <init-param>
       <param-name>storage.endpoint</param-name>