changeset 615:891cb7a0de3a

Implement Storage.updatePojo() Reviewed-by: omajid Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-September/003194.html
author Roman Kennke <rkennke@redhat.com>
date Mon, 17 Sep 2012 23:02:53 +0200
parents 4238b97fc746
children 838eb2700f6a
files common/core/src/main/java/com/redhat/thermostat/common/dao/AgentInfoConverter.java common/core/src/main/java/com/redhat/thermostat/common/dao/AgentInfoDAOImpl.java common/core/src/main/java/com/redhat/thermostat/common/dao/VmInfoDAOImpl.java common/core/src/main/java/com/redhat/thermostat/common/model/VmInfo.java common/core/src/main/java/com/redhat/thermostat/common/storage/MongoStorage.java common/core/src/main/java/com/redhat/thermostat/common/storage/MongoUpdate.java common/core/src/main/java/com/redhat/thermostat/common/storage/Storage.java common/core/src/main/java/com/redhat/thermostat/common/storage/Update.java common/core/src/test/java/com/redhat/thermostat/common/dao/AgentInfoConverterTest.java common/core/src/test/java/com/redhat/thermostat/common/dao/AgentInfoDAOTest.java common/core/src/test/java/com/redhat/thermostat/common/dao/VmInfoDAOTest.java common/core/src/test/java/com/redhat/thermostat/common/storage/MongoStorageTest.java common/core/src/test/java/com/redhat/thermostat/common/storage/UpdateTestHelper.java
diffstat 13 files changed, 289 insertions(+), 206 deletions(-) [+]
line wrap: on
line diff
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/AgentInfoConverter.java	Fri Sep 14 19:24:10 2012 -0400
+++ /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.dao;
-
-import com.redhat.thermostat.common.model.AgentInformation;
-import com.redhat.thermostat.common.storage.Chunk;
-import com.redhat.thermostat.common.storage.Key;
-
-public class AgentInfoConverter {
-
-    public Chunk toChunk(AgentInformation agentInfo) {
-        Chunk agentChunk = new Chunk(AgentInfoDAO.CATEGORY, true);
-
-        agentChunk.put(Key.AGENT_ID, agentInfo.getAgentId());
-        agentChunk.put(AgentInfoDAO.START_TIME_KEY, agentInfo.getStartTime());
-        agentChunk.put(AgentInfoDAO.STOP_TIME_KEY, agentInfo.getStopTime());
-        agentChunk.put(AgentInfoDAO.ALIVE_KEY, agentInfo.isAlive());
-        agentChunk.put(AgentInfoDAO.CONFIG_LISTEN_ADDRESS, agentInfo.getConfigListenAddress());
-
-        return agentChunk;
-    }
-
-    public AgentInformation fromChunk(Chunk agentChunk) {
-        AgentInformation agentInfo = new AgentInformation();
-
-        agentInfo.setAgentId(agentChunk.get(Key.AGENT_ID));
-        agentInfo.setStartTime(agentChunk.get(AgentInfoDAO.START_TIME_KEY));
-        agentInfo.setStopTime(agentChunk.get(AgentInfoDAO.STOP_TIME_KEY));
-        agentInfo.setAlive(agentChunk.get(AgentInfoDAO.ALIVE_KEY));
-        agentInfo.setConfigListenAddress(agentChunk.get(AgentInfoDAO.CONFIG_LISTEN_ADDRESS));
-
-        return agentInfo;
-    }
-}
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/AgentInfoDAOImpl.java	Fri Sep 14 19:24:10 2012 -0400
+++ b/common/core/src/main/java/com/redhat/thermostat/common/dao/AgentInfoDAOImpl.java	Mon Sep 17 23:02:53 2012 +0200
@@ -46,11 +46,11 @@
 import com.redhat.thermostat.common.storage.Query;
 import com.redhat.thermostat.common.storage.Query.Criteria;
 import com.redhat.thermostat.common.storage.Storage;
+import com.redhat.thermostat.common.storage.Update;
 
 public class AgentInfoDAOImpl implements AgentInfoDAO {
 
     private final Storage storage;
-    private final AgentInfoConverter converter = new AgentInfoConverter();
 
     public AgentInfoDAOImpl(Storage storage) {
         this.storage = storage;
@@ -115,7 +115,12 @@
 
     @Override
     public void updateAgentInformation(AgentInformation agentInfo) {
-        storage.updateChunk(converter.toChunk(agentInfo));
+        Update update = storage.createUpdate().from(CATEGORY).where(Key.AGENT_ID, agentInfo.getAgentId())
+                                .set(START_TIME_KEY, agentInfo.getStartTime())
+                                .set(STOP_TIME_KEY, agentInfo.getStopTime())
+                                .set(ALIVE_KEY, agentInfo.isAlive())
+                                .set(CONFIG_LISTEN_ADDRESS, agentInfo.getConfigListenAddress());
+        storage.updatePojo(update);
     }
 
 }
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/VmInfoDAOImpl.java	Fri Sep 14 19:24:10 2012 -0400
+++ b/common/core/src/main/java/com/redhat/thermostat/common/dao/VmInfoDAOImpl.java	Mon Sep 17 23:02:53 2012 +0200
@@ -41,12 +41,12 @@
 import java.util.List;
 
 import com.redhat.thermostat.common.model.VmInfo;
-import com.redhat.thermostat.common.storage.Chunk;
 import com.redhat.thermostat.common.storage.Cursor;
 import com.redhat.thermostat.common.storage.Key;
 import com.redhat.thermostat.common.storage.Query;
 import com.redhat.thermostat.common.storage.Query.Criteria;
 import com.redhat.thermostat.common.storage.Storage;
+import com.redhat.thermostat.common.storage.Update;
 
 class VmInfoDAOImpl implements VmInfoDAO {
 
@@ -115,13 +115,10 @@
 
     @Override
     public void putVmStoppedTime(int vmId, long timestamp) {
-        storage.updateChunk(makeStoppedChunk(vmId, timestamp));
+        Update update = storage.createUpdate().from(vmInfoCategory)
+                                              .where(Key.VM_ID, vmId)
+                                              .set(VmInfoDAO.stopTimeKey, timestamp);
+        storage.updatePojo(update);
     }
 
-    private Chunk makeStoppedChunk(int vmId, long stopTimeStamp) {
-        Chunk chunk = new Chunk(VmInfoDAO.vmInfoCategory, false);
-        chunk.put(Key.VM_ID, vmId);
-        chunk.put(VmInfoDAO.stopTimeKey, stopTimeStamp);
-        return chunk;
-    }
 }
--- a/common/core/src/main/java/com/redhat/thermostat/common/model/VmInfo.java	Fri Sep 14 19:24:10 2012 -0400
+++ b/common/core/src/main/java/com/redhat/thermostat/common/model/VmInfo.java	Mon Sep 17 23:02:53 2012 +0200
@@ -93,7 +93,7 @@
 
     @Persist
     public void setVmId(int vmId) {
-        this.vmPid = vmPid;
+        this.vmPid = vmId;
     }
 
     @Persist
--- a/common/core/src/main/java/com/redhat/thermostat/common/storage/MongoStorage.java	Fri Sep 14 19:24:10 2012 -0400
+++ b/common/core/src/main/java/com/redhat/thermostat/common/storage/MongoStorage.java	Mon Sep 17 23:02:53 2012 +0200
@@ -195,11 +195,19 @@
     }
 
     @Override
-    public void updateChunk(Chunk chunk) {
+    public void updatePojo(Update update) {
+        assert update instanceof MongoUpdate;
+        MongoUpdate mongoUpdate = (MongoUpdate) update;
+        Chunk chunk = mongoUpdate.getChunk();
+        updateChunk(chunk);
+    }
+
+    void updateChunk(Chunk chunk) {
         Category cat = chunk.getCategory();
         DBCollection coll = getCachedCollection(cat.getName());
         BasicDBObject toUpdate = new BasicDBObject();
         BasicDBObject updateKey = getAgentQueryKeyFromChunkOrGlobalAgent(chunk);
+        BasicDBObject setObj = null;
         Map<String, BasicDBObject> nestedParts = new HashMap<String, BasicDBObject>();
         Map<String, BasicDBObject> updateKeyNestedParts = new HashMap<String, BasicDBObject>();
         for (Key<?> key : cat.getKeys()) {
@@ -220,7 +228,11 @@
                         }
                         updateKeyNested.append(entryParts[1], updateKeyNested);
                     } else {
-                        nested.append(SET_MODIFIER, new BasicDBObject(entryParts[1], chunk.get(key)));
+                        if (setObj == null) {
+                            setObj = new BasicDBObject();
+                            nested.append(SET_MODIFIER, setObj);
+                        }
+                        setObj.append(entryParts[1], chunk.get(key));
                     }
                 }
             } else {
@@ -236,7 +248,11 @@
                         if (isKey) {
                             updateKey.append(mongoKey, value);
                         } else {
-                            toUpdate.append(SET_MODIFIER, new BasicDBObject(mongoKey, value));
+                            if (setObj == null) {
+                                setObj = new BasicDBObject();
+                                toUpdate.append(SET_MODIFIER, setObj);
+                            }
+                            setObj.append(mongoKey, value);
                         }
                     }
                 }
@@ -315,6 +331,11 @@
     }
 
     @Override
+    public Update createUpdate() {
+        return new MongoUpdate();
+    }
+
+    @Override
     public <T extends Pojo> Cursor<T> findAllPojos(Query query, Class<T> resultClass) {
         MongoQuery mongoQuery =  checkAndCastQuery(query);
         DBCollection coll = getCachedCollection(mongoQuery.getCollectionName());
@@ -385,6 +406,12 @@
 
     @Override
     public void putPojo(Category category, boolean replace, Pojo pojo) {
+        Chunk chunk = convertPojoToChunk(category, replace, pojo);
+        putChunk(chunk);
+    }
+
+    private Chunk convertPojoToChunk(Category category, boolean replace,
+            Pojo pojo) {
         Converter customConverter = converters.get(pojo.getClass());
         Chunk chunk;
         if (customConverter != null) {
@@ -392,7 +419,7 @@
         } else {
             chunk = new ChunkAdapter(pojo, category, replace);
         }
-        putChunk(chunk);
+        return chunk;
     }
 
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/core/src/main/java/com/redhat/thermostat/common/storage/MongoUpdate.java	Mon Sep 17 23:02:53 2012 +0200
@@ -0,0 +1,78 @@
+/*
+ * 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;
+
+// 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 Chunk updateChunk;
+
+    @Override
+    public Update from(Category category) {
+        if (updateChunk != null) {
+            throw new IllegalStateException();
+        }
+        updateChunk = new Chunk(category, false);
+        return this;
+    }
+
+    @Override
+    public <T> Update where(Key<T> key, T value) {
+        if (updateChunk == null) {
+            throw new IllegalStateException();
+        }
+        updateChunk.put(key, value);
+        return this;
+    }
+
+    @Override
+    public <T> Update set(Key<T> key, T value) {
+        if (updateChunk == null) {
+            throw new IllegalStateException();
+        }
+        updateChunk.put(key, value);
+        return this;
+    }
+
+    Chunk getChunk() {
+        return updateChunk;
+    }
+
+}
--- a/common/core/src/main/java/com/redhat/thermostat/common/storage/Storage.java	Fri Sep 14 19:24:10 2012 -0400
+++ b/common/core/src/main/java/com/redhat/thermostat/common/storage/Storage.java	Mon Sep 17 23:02:53 2012 +0200
@@ -61,7 +61,7 @@
 
     public abstract void putPojo(Category category, boolean replace, Pojo pojo);
 
-    public abstract void updateChunk(Chunk chunk);
+    public abstract void updatePojo(Update update);
 
     public abstract void removeChunk(Chunk chunk);
 
@@ -83,5 +83,5 @@
     public abstract InputStream loadFile(String filename);
 
     public abstract Query createQuery();
-
+    public abstract Update createUpdate();
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/core/src/main/java/com/redhat/thermostat/common/storage/Update.java	Mon Sep 17 23:02:53 2012 +0200
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+
+package com.redhat.thermostat.common.storage;
+
+public interface Update {
+
+    Update from(Category category);
+    <T> Update where(Key<T> key, T value);
+    <T> Update set(Key<T> key, T value);
+}
--- a/common/core/src/test/java/com/redhat/thermostat/common/dao/AgentInfoConverterTest.java	Fri Sep 14 19:24:10 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,100 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.common.dao;
-
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Test;
-
-import com.redhat.thermostat.common.model.AgentInformation;
-import com.redhat.thermostat.common.storage.Chunk;
-import com.redhat.thermostat.common.storage.Key;
-
-public class AgentInfoConverterTest {
-
-    @Test
-    public void testFromChunk() {
-        final String AGENT_ID = "12345";
-        final boolean ALIVE = true;
-        final long START_TIME = 1234;
-        final long STOP_TIME = 5678;
-        final String CONFIG_ADDRESS = "foobar:666";
-
-        Chunk agentInfoChunk = new Chunk(AgentInfoDAO.CATEGORY, true);
-        agentInfoChunk.put(Key.AGENT_ID, AGENT_ID);
-        agentInfoChunk.put(AgentInfoDAO.ALIVE_KEY, ALIVE);
-        agentInfoChunk.put(AgentInfoDAO.START_TIME_KEY, START_TIME);
-        agentInfoChunk.put(AgentInfoDAO.STOP_TIME_KEY, STOP_TIME);
-        agentInfoChunk.put(AgentInfoDAO.CONFIG_LISTEN_ADDRESS, CONFIG_ADDRESS);
-
-        AgentInfoConverter converter = new AgentInfoConverter();
-        AgentInformation info = converter.fromChunk(agentInfoChunk);
-
-        assertEquals(AGENT_ID, info.getAgentId());
-        assertEquals(ALIVE, info.isAlive());
-        assertEquals(START_TIME, info.getStartTime());
-        assertEquals(STOP_TIME, info.getStopTime());
-    }
-
-    @Test
-    public void testToChunk() {
-        final String AGENT_ID = "12345";
-        final boolean ALIVE = true;
-        final long START_TIME = 1234;
-        final long STOP_TIME = 5678;
-        final String CONFIG_ADDRESS = "localhost:666";
-
-        AgentInformation agentInfo = new AgentInformation();
-        agentInfo.setAgentId(AGENT_ID);
-        agentInfo.setAlive(ALIVE);
-        agentInfo.setConfigListenAddress(CONFIG_ADDRESS);
-        agentInfo.setStartTime(START_TIME);
-        agentInfo.setStopTime(STOP_TIME);
-
-        AgentInfoConverter converter = new AgentInfoConverter();
-        Chunk chunk = converter.toChunk(agentInfo);
-
-        assertEquals(AgentInfoDAO.CATEGORY, chunk.getCategory());
-        assertEquals(AGENT_ID, chunk.get(Key.AGENT_ID));
-        assertEquals(ALIVE, chunk.get(AgentInfoDAO.ALIVE_KEY));
-        assertEquals((Long) START_TIME, chunk.get(AgentInfoDAO.START_TIME_KEY));
-        assertEquals((Long) STOP_TIME, chunk.get(AgentInfoDAO.STOP_TIME_KEY));
-        assertEquals((String) CONFIG_ADDRESS, chunk.get(AgentInfoDAO.CONFIG_LISTEN_ADDRESS));
-
-    }
-
-}
--- a/common/core/src/test/java/com/redhat/thermostat/common/dao/AgentInfoDAOTest.java	Fri Sep 14 19:24:10 2012 -0400
+++ b/common/core/src/test/java/com/redhat/thermostat/common/dao/AgentInfoDAOTest.java	Mon Sep 17 23:02:53 2012 +0200
@@ -39,8 +39,10 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertSame;
 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.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import java.util.Collection;
@@ -51,13 +53,14 @@
 import org.mockito.ArgumentCaptor;
 
 import com.redhat.thermostat.common.model.AgentInformation;
-import com.redhat.thermostat.common.model.Pojo;
 import com.redhat.thermostat.common.storage.Category;
 import com.redhat.thermostat.common.storage.Chunk;
 import com.redhat.thermostat.common.storage.Cursor;
 import com.redhat.thermostat.common.storage.Key;
+import com.redhat.thermostat.common.storage.UpdateTestHelper;
 import com.redhat.thermostat.common.storage.Query.Criteria;
 import com.redhat.thermostat.common.storage.Storage;
+import com.redhat.thermostat.common.storage.Update;
 import com.redhat.thermostat.test.MockQuery;
 
 public class AgentInfoDAOTest {
@@ -202,23 +205,23 @@
 
     @Test
     public void verifyUpdateAgentInformation() {
+
+        Update mockUpdate = UpdateTestHelper.createMockUpdate();
         Storage storage = mock(Storage.class);
+        when(storage.createUpdate()).thenReturn(mockUpdate);
         AgentInfoDAO dao = new AgentInfoDAOImpl(storage);
 
         dao.updateAgentInformation(agentInfo1);
 
-        ArgumentCaptor<Chunk> pojoCaptor = ArgumentCaptor.forClass(Chunk.class);
-        verify(storage).updateChunk(pojoCaptor.capture());
-
-        Chunk updatedValue = pojoCaptor.getValue();
+        verify(mockUpdate).from(AgentInfoDAO.CATEGORY);
+        verify(mockUpdate).where(Key.AGENT_ID, "1234");
+        verify(mockUpdate).set(AgentInfoDAO.START_TIME_KEY, 100L);
+        verify(mockUpdate).set(AgentInfoDAO.STOP_TIME_KEY, 10L);
+        verify(mockUpdate).set(AgentInfoDAO.CONFIG_LISTEN_ADDRESS, "foobar:666");
+        verify(mockUpdate).set(AgentInfoDAO.ALIVE_KEY, true);
+        verifyNoMoreInteractions(mockUpdate);
+        verify(storage).updatePojo(mockUpdate);
 
-        Chunk expected = new Chunk(AgentInfoDAO.CATEGORY, true);
-        expected.put(Key.AGENT_ID, "1234");
-        expected.put(AgentInfoDAO.ALIVE_KEY, true);
-        expected.put(AgentInfoDAO.CONFIG_LISTEN_ADDRESS, "foobar:666");
-        expected.put(AgentInfoDAO.START_TIME_KEY, 100L);
-        expected.put(AgentInfoDAO.STOP_TIME_KEY, 10L);
-        assertEquals(expected, updatedValue);
     }
 
     @Test
--- a/common/core/src/test/java/com/redhat/thermostat/common/dao/VmInfoDAOTest.java	Fri Sep 14 19:24:10 2012 -0400
+++ b/common/core/src/test/java/com/redhat/thermostat/common/dao/VmInfoDAOTest.java	Mon Sep 17 23:02:53 2012 +0200
@@ -36,10 +36,13 @@
 
 package com.redhat.thermostat.common.dao;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import java.util.ArrayList;
@@ -50,7 +53,6 @@
 
 import org.junit.Before;
 import org.junit.Test;
-import org.mockito.ArgumentCaptor;
 
 import com.redhat.thermostat.common.model.VmInfo;
 import com.redhat.thermostat.common.storage.Category;
@@ -60,6 +62,8 @@
 import com.redhat.thermostat.common.storage.Query;
 import com.redhat.thermostat.common.storage.Query.Criteria;
 import com.redhat.thermostat.common.storage.Storage;
+import com.redhat.thermostat.common.storage.Update;
+import com.redhat.thermostat.common.storage.UpdateTestHelper;
 import com.redhat.thermostat.test.MockQuery;
 
 public class VmInfoDAOTest {
@@ -279,16 +283,16 @@
 
     @Test
     public void testPutVmStoppedTime() {
+        Update mockUpdate = UpdateTestHelper.createMockUpdate();
         Storage storage = mock(Storage.class);
+        when(storage.createUpdate()).thenReturn(mockUpdate);
         VmInfoDAO dao = new VmInfoDAOImpl(storage);
         dao.putVmStoppedTime(vmId, stopTime);
 
-        ArgumentCaptor<Chunk> arg = ArgumentCaptor.forClass(Chunk.class);
-        verify(storage).updateChunk(arg.capture());
-        Chunk chunk = arg.getValue();
-
-        assertEquals(VmInfoDAO.vmInfoCategory, chunk.getCategory());
-        assertEquals((Integer) vmId, chunk.get(Key.VM_ID));
-        assertEquals((Long) stopTime, chunk.get(VmInfoDAO.stopTimeKey));
+        verify(mockUpdate).from(VmInfoDAO.vmInfoCategory);
+        verify(mockUpdate).where(Key.VM_ID, 1);
+        verify(mockUpdate).set(VmInfoDAO.stopTimeKey, 3L);
+        verifyNoMoreInteractions(mockUpdate);
+        verify(storage).updatePojo(mockUpdate);
     }
 }
--- a/common/core/src/test/java/com/redhat/thermostat/common/storage/MongoStorageTest.java	Fri Sep 14 19:24:10 2012 -0400
+++ b/common/core/src/test/java/com/redhat/thermostat/common/storage/MongoStorageTest.java	Mon Sep 17 23:02:53 2012 +0200
@@ -444,4 +444,42 @@
         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"));
+    }
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/core/src/test/java/com/redhat/thermostat/common/storage/UpdateTestHelper.java	Mon Sep 17 23:02:53 2012 +0200
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+
+package com.redhat.thermostat.common.storage;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class UpdateTestHelper {
+
+    @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;
+    }
+}