changeset 901:eef06eccb01b

Un-fluentize Storage.update(). Reviewed-by: jerboaa Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-January/005034.html
author Roman Kennke <rkennke@redhat.com>
date Thu, 10 Jan 2013 15:02:44 +0100
parents 916ea399eb6b
children 4de458324cf9
files client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMListFormatter.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/test/java/com/redhat/thermostat/common/dao/AgentInfoDAOTest.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 storage/core/src/main/java/com/redhat/thermostat/storage/core/QueuedStorage.java storage/core/src/main/java/com/redhat/thermostat/storage/core/Storage.java storage/core/src/main/java/com/redhat/thermostat/storage/core/Update.java storage/core/src/test/java/com/redhat/thermostat/storage/core/QueuedStorageTest.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/MongoUpdate.java storage/mongo/src/test/java/com/redhat/thermostat/storage/mongodb/internal/MongoStorageTest.java web/client/src/main/java/com/redhat/thermostat/web/client/internal/WebStorage.java web/client/src/test/java/com/redhat/thermostat/web/client/internal/WebStorageTest.java web/common/src/main/java/com/redhat/thermostat/web/common/WebUpdate.java web/server/src/main/java/com/redhat/thermostat/web/server/WebStorageEndPoint.java web/server/src/test/java/com/redhat/thermostat/web/server/QueryTestHelper.java web/server/src/test/java/com/redhat/thermostat/web/server/WebStorageEndpointTest.java
diffstat 19 files changed, 181 insertions(+), 156 deletions(-) [+]
line wrap: on
line diff
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMListFormatter.java	Thu Jan 10 14:53:32 2013 +0100
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMListFormatter.java	Thu Jan 10 15:02:44 2013 +0100
@@ -76,7 +76,6 @@
     }
 
     private void printVM(VmRef vm, VmInfo info) {
-        System.err.println("agentId: " + vm.getAgent().getAgentId());
         printLine(vm.getAgent().getAgentId(),
                   vm.getAgent().getHostName(),
                   vm.getId().toString(),
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/AgentInfoDAOImpl.java	Thu Jan 10 14:53:32 2013 +0100
+++ b/common/core/src/main/java/com/redhat/thermostat/common/dao/AgentInfoDAOImpl.java	Thu Jan 10 15:02:44 2013 +0100
@@ -118,12 +118,13 @@
 
     @Override
     public void updateAgentInformation(AgentInformation 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);
+        Update update = storage.createUpdate(CATEGORY);
+        update.where(Key.AGENT_ID, agentInfo.getAgentId());
+        update.set(START_TIME_KEY, agentInfo.getStartTime());
+        update.set(STOP_TIME_KEY, agentInfo.getStopTime());
+        update.set(ALIVE_KEY, agentInfo.isAlive());
+        update.set(CONFIG_LISTEN_ADDRESS, agentInfo.getConfigListenAddress());
+        update.apply();
     }
 
 }
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/VmInfoDAOImpl.java	Thu Jan 10 14:53:32 2013 +0100
+++ b/common/core/src/main/java/com/redhat/thermostat/common/dao/VmInfoDAOImpl.java	Thu Jan 10 15:02:44 2013 +0100
@@ -119,10 +119,10 @@
 
     @Override
     public void putVmStoppedTime(int vmId, long timestamp) {
-        Update update = storage.createUpdate().from(vmInfoCategory)
-                                              .where(Key.VM_ID, vmId)
-                                              .set(VmInfoDAO.stopTimeKey, timestamp);
-        storage.updatePojo(update);
+        Update update = storage.createUpdate(vmInfoCategory);
+        update.where(Key.VM_ID, vmId);
+        update.set(VmInfoDAO.stopTimeKey, timestamp);
+        update.apply();
     }
 
 }
--- a/common/core/src/test/java/com/redhat/thermostat/common/dao/AgentInfoDAOTest.java	Thu Jan 10 14:53:32 2013 +0100
+++ b/common/core/src/test/java/com/redhat/thermostat/common/dao/AgentInfoDAOTest.java	Thu Jan 10 15:02:44 2013 +0100
@@ -212,21 +212,21 @@
     @Test
     public void verifyUpdateAgentInformation() {
 
-        Update mockUpdate = QueryTestHelper.createMockUpdate();
+        Update mockUpdate = mock(Update.class);
         Storage storage = mock(Storage.class);
-        when(storage.createUpdate()).thenReturn(mockUpdate);
+        when(storage.createUpdate(any(Category.class))).thenReturn(mockUpdate);
         AgentInfoDAO dao = new AgentInfoDAOImpl(storage);
 
         dao.updateAgentInformation(agentInfo1);
 
-        verify(mockUpdate).from(AgentInfoDAO.CATEGORY);
+        verify(storage).createUpdate(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);
+        verify(mockUpdate).apply();
         verifyNoMoreInteractions(mockUpdate);
-        verify(storage).updatePojo(mockUpdate);
 
     }
 
--- a/common/core/src/test/java/com/redhat/thermostat/common/dao/QueryTestHelper.java	Thu Jan 10 14:53:32 2013 +0100
+++ b/common/core/src/test/java/com/redhat/thermostat/common/dao/QueryTestHelper.java	Thu Jan 10 15:02:44 2013 +0100
@@ -44,22 +44,12 @@
 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.Query.Criteria;
 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);
--- a/common/core/src/test/java/com/redhat/thermostat/common/dao/VmInfoDAOTest.java	Thu Jan 10 14:53:32 2013 +0100
+++ b/common/core/src/test/java/com/redhat/thermostat/common/dao/VmInfoDAOTest.java	Thu Jan 10 15:02:44 2013 +0100
@@ -270,16 +270,17 @@
 
     @Test
     public void testPutVmStoppedTime() {
-        Update mockUpdate = QueryTestHelper.createMockUpdate();
+        Update mockUpdate = mock(Update.class);
         Storage storage = mock(Storage.class);
-        when(storage.createUpdate()).thenReturn(mockUpdate);
+        when(storage.createUpdate(any(Category.class))).thenReturn(mockUpdate);
+
         VmInfoDAO dao = new VmInfoDAOImpl(storage);
         dao.putVmStoppedTime(vmId, stopTime);
 
-        verify(mockUpdate).from(VmInfoDAO.vmInfoCategory);
+        verify(storage).createUpdate(VmInfoDAO.vmInfoCategory);
         verify(mockUpdate).where(Key.VM_ID, 1);
         verify(mockUpdate).set(VmInfoDAO.stopTimeKey, 3L);
+        verify(mockUpdate).apply();
         verifyNoMoreInteractions(mockUpdate);
-        verify(storage).updatePojo(mockUpdate);
     }
 }
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/core/QueuedStorage.java	Thu Jan 10 14:53:32 2013 +0100
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/core/QueuedStorage.java	Thu Jan 10 15:02:44 2013 +0100
@@ -64,6 +64,38 @@
         
     }
 
+    private class QueuedUpdate implements Update {
+        private Update delegateUpdate;
+
+        QueuedUpdate(Update delegateUpdate) {
+            this.delegateUpdate = delegateUpdate;
+        }
+
+        @Override
+        public <T> void where(Key<T> key, T value) {
+            delegateUpdate.where(key,  value);
+            
+        }
+
+        @Override
+        public <T> void set(Key<T> key, T value) {
+            delegateUpdate.set(key, value);
+        }
+
+        @Override
+        public void apply() {
+            executor.execute(new Runnable() {
+                
+                @Override
+                public void run() {
+                    delegateUpdate.apply();
+                }
+
+            });
+        }
+
+    }
+
     private Storage delegate;
     private ExecutorService executor;
     private ExecutorService fileExecutor;
@@ -140,20 +172,6 @@
     }
 
     @Override
-    public void updatePojo(final Update update) {
-
-        executor.execute(new Runnable() {
-            
-            @Override
-            public void run() {
-                delegate.updatePojo(update);
-            }
-
-        });
-
-    }
-
-    @Override
     public void removePojo(final Remove remove) {
 
         executor.execute(new Runnable() {
@@ -221,8 +239,9 @@
     }
 
     @Override
-    public Update createUpdate() {
-        return delegate.createUpdate();
+    public Update createUpdate(Category category) {
+        QueuedUpdate update = new QueuedUpdate(delegate.createUpdate(category));
+        return update;
     }
 
     @Override
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/core/Storage.java	Thu Jan 10 14:53:32 2013 +0100
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/core/Storage.java	Thu Jan 10 15:02:44 2013 +0100
@@ -61,8 +61,6 @@
     Add createAdd(Category category);
     Replace createReplace(Category category);
 
-    void updatePojo(Update update);
-
     void removePojo(Remove remove);
 
     /**
@@ -81,7 +79,7 @@
     InputStream loadFile(String filename);
 
     Query createQuery();
-    Update createUpdate();
+    Update createUpdate(Category category);
     Remove createRemove();
 
 
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/core/Update.java	Thu Jan 10 14:53:32 2013 +0100
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/core/Update.java	Thu Jan 10 15:02:44 2013 +0100
@@ -37,11 +37,35 @@
 package com.redhat.thermostat.storage.core;
 
 /**
- * Describes which data should be updated with what values
+ * Updates fields of a database entry. 
  */
 public interface Update {
 
-    Update from(Category category);
-    <T> Update where(Key<T> key, T value);
-    <T> Update set(Key<T> key, T value);
+    /**
+     * Adds a where clause that denotes the entry to be updated. If more than one
+     * where-clause is declared, they are concatenated as an and-query.
+     * If a clause with the same key is declared more than once, the latter
+     * overrides the former. This is so that an Update object can be reused
+     * for multiple requests. If an update is issued for which no entry can
+     * be found (i.e. the where-clause yields no results), a
+     * <code>StorageException</code> may get thrown.
+     *
+     * @param key the key of the field of the where clause
+     * @param value the value of the field of the where clause
+     */
+    <T> void where(Key<T> key, T value);
+
+    /**
+     * Sets a field in a found document to the specified value. If the same key is
+     * set more than once, the latest values overrides the former values.
+     *
+     * @param key the key of the field
+     * @param value the value to set
+     */
+    <T> void set(Key<T> key, T value);
+
+    /**
+     * Applies the update operation.
+     */
+    void apply();
 }
--- a/storage/core/src/test/java/com/redhat/thermostat/storage/core/QueuedStorageTest.java	Thu Jan 10 14:53:32 2013 +0100
+++ b/storage/core/src/test/java/com/redhat/thermostat/storage/core/QueuedStorageTest.java	Thu Jan 10 15:02:44 2013 +0100
@@ -194,12 +194,10 @@
         delegateAdd = mock(Add.class);
         delegateReplace = mock(Replace.class);
 
-        Update update = mock(Update.class);
         Remove remove = mock(Remove.class);
         Query query = mock(Query.class);
         when(delegateStorage.createAdd(any(Category.class))).thenReturn(delegateAdd);
         when(delegateStorage.createReplace(any(Category.class))).thenReturn(delegateReplace);
-        when(delegateStorage.createUpdate()).thenReturn(update);
         when(delegateStorage.createRemove()).thenReturn(remove);
         when(delegateStorage.createQuery()).thenReturn(query);
         expectedResults = mock(Cursor.class);
@@ -250,19 +248,23 @@
 
     @Test
     public void testUpdatePojo() {
+        Update delegateUpdate = mock(Update.class);
+        when(delegateStorage.createUpdate(any(Category.class))).thenReturn(delegateUpdate);
 
-        Update update = queuedStorage.createUpdate();
-        verify(delegateStorage).createUpdate();
+        Category category = mock(Category.class);
+
+        Update update = queuedStorage.createUpdate(category);
+        verify(delegateStorage).createUpdate(category);
         verifyNoMoreInteractions(delegateStorage);
 
-        queuedStorage.updatePojo(update);
+        update.apply();
 
         Runnable r = executor.getTask();
         assertNotNull(r);
-        verifyZeroInteractions(delegateStorage);
+        verifyZeroInteractions(delegateUpdate);
         r.run();
-        verify(delegateStorage, times(1)).updatePojo(update);
-        verifyNoMoreInteractions(delegateStorage);
+        verify(delegateUpdate).apply();
+        verifyNoMoreInteractions(delegateUpdate);
 
         assertNull(fileExecutor.getTask());
     }
--- a/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoStorage.java	Thu Jan 10 14:53:32 2013 +0100
+++ b/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoStorage.java	Thu Jan 10 15:02:44 2013 +0100
@@ -75,8 +75,6 @@
  */
 public class MongoStorage implements Storage {
 
-    public static final String SET_MODIFIER = "$set";
-
     private class MongoAdd extends BasePut implements Add {
 
         @Override
@@ -177,10 +175,7 @@
         return toInsert;
     }
 
-    @Override
-    public void updatePojo(Update update) {
-        assert update instanceof MongoUpdate;
-        MongoUpdate mongoUpdate = (MongoUpdate) update;
+    void updatePojo(MongoUpdate mongoUpdate) {
         Category cat = mongoUpdate.getCategory();
         DBCollection coll = getCachedCollection(cat);
         DBObject query = mongoUpdate.getQuery();
@@ -246,8 +241,8 @@
     }
 
     @Override
-    public Update createUpdate() {
-        return new MongoUpdate();
+    public Update createUpdate(Category category) {
+        return new MongoUpdate(this, category);
     }
 
     @Override
--- a/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoUpdate.java	Thu Jan 10 14:53:32 2013 +0100
+++ b/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoUpdate.java	Thu Jan 10 15:02:44 2013 +0100
@@ -43,22 +43,18 @@
 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 static final String SET_MODIFIER = "$set";
+
+    private MongoStorage storage;
     private DBObject query;
     private DBObject values;
     private Category category;
 
-    @Override
-    public Update from(Category category) {
-        if (query != null || values != null) {
-            throw new IllegalStateException();
-        }
+    public MongoUpdate(MongoStorage storage, Category category) {
+        this.storage = storage;
         this.category = category;
-        return this;
     }
 
     Category getCategory() {
@@ -66,12 +62,11 @@
     }
 
     @Override
-    public <T> Update where(Key<T> key, T value) {
+    public <T> void where(Key<T> key, T value) {
         if (query == null) {
             query = new BasicDBObject();
         }
         query.put(key.getName(), value);
-        return this;
     }
 
     DBObject getQuery() {
@@ -79,15 +74,19 @@
     }
 
     @Override
-    public <T> Update set(Key<T> key, T value) {
+    public <T> void 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);
+        return new BasicDBObject(SET_MODIFIER, values);
+    }
+
+    @Override
+    public void apply() {
+        storage.updatePojo(this);
     }
 }
--- a/storage/mongo/src/test/java/com/redhat/thermostat/storage/mongodb/internal/MongoStorageTest.java	Thu Jan 10 14:53:32 2013 +0100
+++ b/storage/mongo/src/test/java/com/redhat/thermostat/storage/mongodb/internal/MongoStorageTest.java	Thu Jan 10 15:02:44 2013 +0100
@@ -460,8 +460,10 @@
     @Test
     public void verifySimpleUpdate() {
         MongoStorage storage = makeStorage();
-        Update update = storage.createUpdate().from(testCategory).where(Key.AGENT_ID, "test1").set(key2, "test2");
-        storage.updatePojo(update);
+        Update update = storage.createUpdate(testCategory);
+        update.where(Key.AGENT_ID, "test1");
+        update.set(key2, "test2");
+        update.apply();
 
         ArgumentCaptor<DBObject> queryCaptor = ArgumentCaptor.forClass(DBObject.class);
         ArgumentCaptor<DBObject> valueCaptor = ArgumentCaptor.forClass(DBObject.class);
@@ -470,13 +472,24 @@
         DBObject query = queryCaptor.getValue();
         assertTrue(query.containsField(Key.AGENT_ID.getName()));
         assertEquals("test1", query.get(Key.AGENT_ID.getName()));
+
+        DBObject set = valueCaptor.getValue();
+        assertEquals(1, set.keySet().size());
+        assertTrue(set.containsField("$set"));
+        DBObject values = (DBObject) set.get("$set");
+        assertEquals(1, values.keySet().size());
+        assertTrue(values.containsField(key2.getName()));
+        assertEquals("test2", values.get(key2.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);
+        Update update = storage.createUpdate(testCategory);
+        update.where(Key.AGENT_ID, "test1");
+        update.set(key2, "test2");
+        update.set(key3, "test3");
+        update.apply();
 
         ArgumentCaptor<DBObject> queryCaptor = ArgumentCaptor.forClass(DBObject.class);
         ArgumentCaptor<DBObject> valueCaptor = ArgumentCaptor.forClass(DBObject.class);
@@ -485,9 +498,10 @@
         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");
+
+        DBObject set = valueCaptor.getValue();
+        assertTrue(set.containsField("$set"));
+        DBObject values = (DBObject) set.get("$set");
         assertTrue(values.containsField("key2"));
         assertEquals("test2", values.get("key2"));
         assertTrue(values.containsField("key3"));
--- a/web/client/src/main/java/com/redhat/thermostat/web/client/internal/WebStorage.java	Thu Jan 10 14:53:32 2013 +0100
+++ b/web/client/src/main/java/com/redhat/thermostat/web/client/internal/WebStorage.java	Thu Jan 10 15:02:44 2013 +0100
@@ -296,6 +296,14 @@
         
     }
 
+    private class WebUpdateImpl extends WebUpdate implements Update {
+    
+        @Override
+        public void apply() {
+            updatePojo(this);
+        }
+    }
+
     private String endpoint;
     private UUID agentId;
 
@@ -435,8 +443,10 @@
     }
 
     @Override
-    public WebUpdate createUpdate() {
-        return new WebUpdate(categoryIds);
+    public Update createUpdate(Category category) {
+        WebUpdateImpl updateImpl = new WebUpdateImpl();
+        updateImpl.setCategoryId(categoryIds.get(category));
+        return updateImpl;
     }
 
     @SuppressWarnings("unchecked")
@@ -564,8 +574,7 @@
         this.agentId = agentId;
     }
 
-    @Override
-    public void updatePojo(Update update) throws StorageException {
+    private void updatePojo(Update update) throws StorageException {
         WebUpdate webUp = (WebUpdate) update;
         List<WebUpdate.UpdateValue> updateValues = webUp.getUpdates();
         List<Object> values = new ArrayList<>(updateValues.size());
--- a/web/client/src/test/java/com/redhat/thermostat/web/client/internal/WebStorageTest.java	Thu Jan 10 14:53:32 2013 +0100
+++ b/web/client/src/test/java/com/redhat/thermostat/web/client/internal/WebStorageTest.java	Thu Jan 10 15:02:44 2013 +0100
@@ -90,6 +90,7 @@
 import com.redhat.thermostat.storage.core.Query;
 import com.redhat.thermostat.storage.core.Query.Criteria;
 import com.redhat.thermostat.storage.core.Remove;
+import com.redhat.thermostat.storage.core.Update;
 import com.redhat.thermostat.test.FreePortFinder;
 import com.redhat.thermostat.test.FreePortFinder.TryPort;
 import com.redhat.thermostat.web.common.Qualifier;
@@ -362,21 +363,19 @@
 
     @Test
     public void testCreateUpdate() {
-        WebUpdate update = (WebUpdate) storage.createUpdate();
+        WebUpdate update = (WebUpdate) storage.createUpdate(category);
         assertNotNull(update);
-        update = update.from(category);
         assertEquals(42, update.getCategoryId());
-        assertNotNull(update);
-        update = update.where(key1, "test");
-        assertNotNull(update);
+
+        update.where(key1, "test");
         List<Qualifier<?>> qualifiers = update.getQualifiers();
         assertEquals(1, qualifiers.size());
         Qualifier<?> qualifier = qualifiers.get(0);
         assertEquals(key1, qualifier.getKey());
         assertEquals(Criteria.EQUALS, qualifier.getCriteria());
         assertEquals("test", qualifier.getValue());
-        update = update.set(key1, "fluff");
-        assertNotNull(update);
+
+        update.set(key1, "fluff");
         List<WebUpdate.UpdateValue> updates = update.getUpdates();
         assertEquals(1, updates.size());
         assertEquals("fluff", updates.get(0).getValue());
@@ -387,8 +386,11 @@
     @Test
     public void testUpdate() throws UnsupportedEncodingException, IOException, JsonSyntaxException, ClassNotFoundException {
 
-        WebUpdate update = storage.createUpdate().from(category).where(key1, "test").set(key1, "fluff").set(key2, 42);
-        storage.updatePojo(update);
+        Update update = storage.createUpdate(category);
+        update.where(key1, "test");
+        update.set(key1, "fluff");
+        update.set(key2, 42);
+        update.apply();
 
         Gson gson = new Gson();
         StringReader reader = new StringReader(requestBody);
--- a/web/common/src/main/java/com/redhat/thermostat/web/common/WebUpdate.java	Thu Jan 10 14:53:32 2013 +0100
+++ b/web/common/src/main/java/com/redhat/thermostat/web/common/WebUpdate.java	Thu Jan 10 15:02:44 2013 +0100
@@ -38,14 +38,11 @@
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 
-import com.redhat.thermostat.storage.core.Category;
 import com.redhat.thermostat.storage.core.Key;
-import com.redhat.thermostat.storage.core.Update;
 import com.redhat.thermostat.storage.core.Query.Criteria;
 
-public class WebUpdate implements Update {
+public class WebUpdate {
 
     public static class UpdateValue {
         private Key<?> key;
@@ -90,44 +87,31 @@
 
     }
 
-    private transient Map<Category, Integer> categoryIds;
-    private Integer categoryId;
+    private int categoryId;
     private List<Qualifier<?>> qualifiers;
     private List<UpdateValue> updateValues;
 
-    // NOTE: This is needed for de-serialization!
     public WebUpdate() {
-        this(null);
-    }
-
-    public WebUpdate(Map<Category, Integer> categoryIds) {
         qualifiers = new ArrayList<>();
         updateValues = new ArrayList<>();
-        this.categoryIds = categoryIds;
     }
 
-    @Override
-    public WebUpdate from(Category category) {
-        categoryId = categoryIds.get(category);
-        return this;
+    public <T> void where(Key<T> key, T value) {
+        qualifiers.add(new Qualifier<T>(key, Criteria.EQUALS, value));
     }
 
-    @Override
-    public <T> WebUpdate where(Key<T> key, T value) {
-        qualifiers.add(new Qualifier<T>(key, Criteria.EQUALS, value));
-        return this;
-    }
-
-    @Override
-    public <T> WebUpdate set(Key<T> key, T value) {
+    public <T> void set(Key<T> key, T value) {
         updateValues.add(new UpdateValue(key, value));
-        return this;
     }
 
     public int getCategoryId() {
         return categoryId;
     }
 
+    public void setCategoryId(int categoryId) {
+        this.categoryId = categoryId;
+    }
+
     public List<Qualifier<?>> getQualifiers() {
         return qualifiers;
     }
--- a/web/server/src/main/java/com/redhat/thermostat/web/server/WebStorageEndPoint.java	Thu Jan 10 14:53:32 2013 +0100
+++ b/web/server/src/main/java/com/redhat/thermostat/web/server/WebStorageEndPoint.java	Thu Jan 10 15:02:44 2013 +0100
@@ -284,12 +284,11 @@
         try {
             String updateParam = req.getParameter("update");
             WebUpdate update = gson.fromJson(updateParam, WebUpdate.class);
-            Update targetUpdate = storage.createUpdate();
-            targetUpdate = targetUpdate.from(getCategoryFromId(update.getCategoryId()));
+            Update targetUpdate = storage.createUpdate(getCategoryFromId(update.getCategoryId()));
             List<Qualifier<?>> qualifiers = update.getQualifiers();
             for (Qualifier qualifier : qualifiers) {
                 assert (qualifier.getCriteria() == Criteria.EQUALS);
-                targetUpdate = targetUpdate.where(qualifier.getKey(), qualifier.getValue());
+                targetUpdate.where(qualifier.getKey(), qualifier.getValue());
             }
             List<WebUpdate.UpdateValue> updates = update.getUpdates();
             if (updates != null) {
@@ -308,7 +307,7 @@
                     targetUpdate.set(key, value);
                 }
             }
-            storage.updatePojo(targetUpdate);
+            targetUpdate.apply();
             resp.setStatus(HttpServletResponse.SC_OK);
         } catch (ClassNotFoundException ex) {
             ex.printStackTrace();
--- a/web/server/src/test/java/com/redhat/thermostat/web/server/QueryTestHelper.java	Thu Jan 10 14:53:32 2013 +0100
+++ b/web/server/src/test/java/com/redhat/thermostat/web/server/QueryTestHelper.java	Thu Jan 10 15:02:44 2013 +0100
@@ -44,20 +44,10 @@
 import com.redhat.thermostat.storage.core.Category;
 import com.redhat.thermostat.storage.core.Key;
 import com.redhat.thermostat.storage.core.Remove;
-import com.redhat.thermostat.storage.core.Update;
 
 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);
--- a/web/server/src/test/java/com/redhat/thermostat/web/server/WebStorageEndpointTest.java	Thu Jan 10 14:53:32 2013 +0100
+++ b/web/server/src/test/java/com/redhat/thermostat/web/server/WebStorageEndpointTest.java	Thu Jan 10 15:02:44 2013 +0100
@@ -44,6 +44,7 @@
 import static org.mockito.Matchers.same;
 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.io.ByteArrayInputStream;
@@ -73,8 +74,6 @@
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
-import sun.misc.BASE64Encoder;
-
 import com.google.gson.Gson;
 import com.redhat.thermostat.storage.core.Categories;
 import com.redhat.thermostat.storage.core.Category;
@@ -320,9 +319,8 @@
     }
 
     private void sendAuthorization(HttpURLConnection conn, String username, String passwd) {
-        BASE64Encoder enc = new BASE64Encoder();
         String userpassword = username + ":" + passwd;
-        String encodedAuthorization = enc.encode( userpassword.getBytes() );
+        String encodedAuthorization = Base64.encodeBase64String(userpassword.getBytes());
         conn.setRequestProperty("Authorization", "Basic "+ encodedAuthorization);
     }
 
@@ -362,10 +360,7 @@
     public void testUpdatePojo() throws IOException {
 
         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);
-        when(mockStorage.createUpdate()).thenReturn(mockUpdate);
+        when(mockStorage.createUpdate(any(Category.class))).thenReturn(mockUpdate);
 
         String endpoint = getEndpoint();
 
@@ -373,9 +368,13 @@
         HttpURLConnection conn = (HttpURLConnection) url.openConnection();
         conn.setDoOutput(true);
         conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
-        Map<Category,Integer> categoryIds = new HashMap<>();
-        categoryIds.put(category, categoryId);
-        WebUpdate update = new WebUpdate(categoryIds).from(category).where(key1, "test").set(key1, "fluff").set(key2, 42);
+
+        WebUpdate update = new WebUpdate();
+        update.setCategoryId(categoryId);
+        update.where(key1, "test");
+        update.set(key1, "fluff");
+        update.set(key2, 42);
+
         Gson gson = new Gson();
         OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream());
         out.write("update=");
@@ -386,12 +385,12 @@
         out.flush();
 
         assertEquals(200, conn.getResponseCode());
-        verify(mockStorage).createUpdate();
-        verify(mockUpdate).from(category);
+        verify(mockStorage).createUpdate(category);
         verify(mockUpdate).where(key1, "test");
         verify(mockUpdate).set(key1, "fluff");
         verify(mockUpdate).set(key2, 42);
-        verify(mockStorage).updatePojo(mockUpdate);
+        verify(mockUpdate).apply();
+        verifyNoMoreInteractions(mockUpdate);
     }