changeset 1292:e097ed12e41f

Some Category hardening. Reviewed-by: vanaltj Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-October/008573.html
author Severin Gehwolf <sgehwolf@redhat.com>
date Mon, 28 Oct 2013 15:30:44 +0100
parents 0e01dad5f877
children 64d12b250c46
files storage/core/src/main/java/com/redhat/thermostat/storage/core/Category.java storage/core/src/main/java/com/redhat/thermostat/storage/core/CategoryAdapter.java storage/core/src/main/java/com/redhat/thermostat/storage/core/StatementDescriptor.java storage/core/src/main/java/com/redhat/thermostat/storage/internal/AdaptedCategory.java storage/core/src/test/java/com/redhat/thermostat/storage/core/CategoryTest.java storage/core/src/test/java/com/redhat/thermostat/storage/core/QueuedStorageTest.java storage/core/src/test/java/com/redhat/thermostat/storage/internal/AdaptedCategoryTest.java storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/PreparedStatementImplTest.java web/common/src/test/java/com/redhat/thermostat/web/common/CategorySerializationTest.java web/server/src/test/java/com/redhat/thermostat/web/server/auth/AgentIdFilterTest.java web/server/src/test/java/com/redhat/thermostat/web/server/auth/HostnameFilterTest.java web/server/src/test/java/com/redhat/thermostat/web/server/auth/UserPrincipalTest.java web/server/src/test/java/com/redhat/thermostat/web/server/auth/VmIdFilterTest.java web/server/src/test/java/com/redhat/thermostat/web/server/auth/VmUsernameFilterTest.java
diffstat 14 files changed, 413 insertions(+), 285 deletions(-) [+]
line wrap: on
line diff
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/core/Category.java	Tue Oct 22 18:20:04 2013 +0200
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/core/Category.java	Mon Oct 28 15:30:44 2013 +0100
@@ -45,25 +45,63 @@
 import com.redhat.thermostat.storage.model.Pojo;
 
 /**
- * A bag of data
+ * A description for data persisted in storage. It describes how model objects
+ * are going to be categorized in persistent storage.
+ * 
+ * @param <T>
+ *            The model class used for data mapped to this category.
  */
 public class Category<T extends Pojo> {
 
+    /*
+     * The name of the category. This is an de-facto immutable field set only by
+     * the constructor via setName(). An exception to this rule is
+     * AdaptedCategory and JSON serialization.
+     * 
+     * This field gets serialized via JSON.
+     */
     protected String name;
+    /*
+     * A de-facto unmodifiable map of key-name => key pairs. A key-name may
+     * represent a property in storage. Set via the constructor. Exceptions are
+     * AdaptedCategory and JSON serialization.
+     * 
+     * This key map gets serialized via JSON.
+     */
     protected Map<String, Key<?>> keys;
+    /*
+     * A de-facto immutable field, set via setDataClass() called by the
+     * constructor. If null dataClassName must be set. This is to make Category
+     * JSON serializable.
+     * 
+     * This field does not get serialized. Instead it's name gets serialized.
+     */
     private transient Class<T> dataClass;
+    /*
+     * A de-facto immutable field, set via setDataClass() called by the
+     * constructor. Essentially a buddy-field to dataClass.
+     * 
+     * This field gets serialized via JSON.
+     */
     protected String dataClassName;
-
-    public Category() {
-        this(null, null);
+    
+    /* No-arg Constructor.
+     * 
+     * Used for serialization and - implicitly - by AdaptedCategory
+     */
+    protected Category() {
+        // empty
     }
     
     /**
      * Creates a new Category instance with the specified name.
-     *
-     * @param name the name of the category
-     *
-     * @throws IllegalArgumentException if a Category is created with a name that has been used before
+     * 
+     * @param name
+     *            the name of the category
+     * 
+     * @throws IllegalArgumentException
+     *             if a Category is created with a name that has been used
+     *             before
      */
     public Category(String name, Class<T> dataClass, Key<?>... keys) {
         Map<String, Key<?>> keysMap = new HashMap<String, Key<?>>();
@@ -75,13 +113,17 @@
         setDataClass(dataClass);
     }
 
+    /**
+     * 
+     * @return The category name which uniquely identifies this category.
+     */
     public String getName() {
         return name;
     }
 
     private void setName(String name) {
         if (Categories.contains(name)) {
-            throw new IllegalStateException();
+            throw new IllegalStateException("category " + name + " already created!");
         }
 
         this.name = name;
@@ -114,11 +156,29 @@
         }
     }
 
+    /**
+     * 
+     * @return A collection of {@link Key}s for this category or an empty
+     *         collection if no keys.
+     */
     public synchronized Collection<Key<?>> getKeys() {
+        if (keys == null) {
+            return Collections.emptySet();
+        }
         return keys.values();
     }
 
+    /**
+     * 
+     * @param name
+     *            The name of the key to retrieve.
+     * @return The key with the specified name or {@code null} if there was no
+     *         such key.
+     */
     public Key<?> getKey(String name) {
+        if (keys == null) {
+            return null;
+        }
         return keys.get(name);
     }
 
@@ -127,10 +187,17 @@
         return getName() + "|" + getDataClass().getName() + "|" + keys;
     }
     
+    @Override
     public int hashCode() {
+        /*
+         * The assumption is that name, keys and dataClass are immutable once
+         * created. This occurs either via JSON deserialization, the only public
+         * constructor or AdaptedCategory.
+         */
         return Objects.hash(name, keys, getDataClass());
     }
 
+    @Override
     public boolean equals(Object o) {
         if (! (o instanceof Category)) {
             return false;
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/core/CategoryAdapter.java	Tue Oct 22 18:20:04 2013 +0200
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/core/CategoryAdapter.java	Mon Oct 28 15:30:44 2013 +0100
@@ -48,7 +48,7 @@
  * @param <T> The source data type.
  * @param <S> The target data type after adaptation.
  */
-public class CategoryAdapter<T extends Pojo, S extends Pojo> {
+public final class CategoryAdapter<T extends Pojo, S extends Pojo> {
 
     private final Category<T> sourceCategory;
     
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/core/StatementDescriptor.java	Tue Oct 22 18:20:04 2013 +0200
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/core/StatementDescriptor.java	Mon Oct 28 15:30:44 2013 +0100
@@ -36,7 +36,7 @@
 
 import com.redhat.thermostat.storage.model.Pojo;
 
-public class StatementDescriptor<T extends Pojo> {
+public final class StatementDescriptor<T extends Pojo> {
     
     private final Category<T> category;
     private final String desc;
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/internal/AdaptedCategory.java	Tue Oct 22 18:20:04 2013 +0200
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/internal/AdaptedCategory.java	Mon Oct 28 15:30:44 2013 +0100
@@ -36,6 +36,7 @@
 
 package com.redhat.thermostat.storage.internal;
 
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -51,7 +52,7 @@
  * @param <T> The type to adapt a category to.
  * @param <S> The source type to adapt things from.
  */
-public class AdaptedCategory<T extends Pojo, S extends Pojo> extends Category<T> {
+public final class AdaptedCategory<T extends Pojo, S extends Pojo> extends Category<T> {
 
     /**
      * Constructor used by CategoryAdapter which has just
@@ -65,7 +66,7 @@
         for (Key<?> key: category.getKeys()) {
             mappedKeys.put(key.getName(), key);
         }
-        this.keys = mappedKeys;
+        this.keys = Collections.unmodifiableMap(mappedKeys);
         if (!AggregateResult.class.isAssignableFrom(dataClass)) {
             String msg = "Can only adapt to aggregate results!";
             throw new IllegalArgumentException(msg);
--- a/storage/core/src/test/java/com/redhat/thermostat/storage/core/CategoryTest.java	Tue Oct 22 18:20:04 2013 +0200
+++ b/storage/core/src/test/java/com/redhat/thermostat/storage/core/CategoryTest.java	Mon Oct 28 15:30:44 2013 +0100
@@ -38,9 +38,12 @@
 
 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.assertTrue;
+import static org.junit.Assert.fail;
 
+import java.lang.reflect.Field;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
@@ -116,5 +119,70 @@
         int expectedHash = Objects.hash("testHashCode", keys, TestObj.class);
         assertEquals(expectedHash, category.hashCode());
     }
+    
+    /**
+     * If a Category instance gets serialized we only set the dataClassName.
+     * However, getting the dataClass from the name must still work.
+     */
+    @Test
+    public void testGetDataClassByName() {
+        Category<TestObj> category = new Category<>("testGetDataClassByName", null);
+        // set dataClassName via reflection
+        try {
+            Field dataClassName = Category.class.getDeclaredField("dataClassName");
+            dataClassName.setAccessible(true);
+            dataClassName.set(category, TestObj.class.getName());
+        } catch (Exception e) {
+            e.printStackTrace();
+            fail(e.getMessage());
+        }
+        // now we should be able to get the dataclass itself
+        Class<TestObj> dataClass = category.getDataClass();
+        assertNotNull(dataClass);
+        assertEquals(TestObj.class, dataClass);
+    }
+    
+    @Test
+    public void testHashCodeWithDataClassNotSet() {
+        Category<TestObj> category = new Category<>("testHashCodeWithDataClassNotSet", null);
+        // set dataClassName via reflection
+        try {
+            Field dataClassName = Category.class.getDeclaredField("dataClassName");
+            dataClassName.setAccessible(true);
+            dataClassName.set(category, TestObj.class.getName());
+        } catch (Exception e) {
+            e.printStackTrace();
+            fail(e.getMessage());
+        }
+        // hash code must not change if dataclass gets set internally
+        int firstHashCode = category.hashCode();
+        Class<TestObj> dataClass = category.getDataClass();
+        // hashCode should have initialized dataClass
+        assertNotNull(dataClass);
+        assertEquals(TestObj.class, dataClass);
+        assertEquals(firstHashCode, category.hashCode());
+    }
+    
+    @Test
+    public void getKeysNull() {
+        Category<TestObj> cat = new Category<>();
+        // this must not throw NPE
+        Collection<Key<?>> keys = cat.getKeys();
+        assertTrue(keys.isEmpty());
+        try {
+            keys.add(new Key<>());
+            fail("empty keys must be immutable");
+        } catch (UnsupportedOperationException e) {
+            // pass
+        }
+    }
+    
+    @Test
+    public void getKeyByNameNull() {
+        Category<TestObj> cat = new Category<>();
+        // This must not throw a NPE
+        Key<?> key = cat.getKey("foo-key-not-there");
+        assertNull(key);
+    }
 }
 
--- a/storage/core/src/test/java/com/redhat/thermostat/storage/core/QueuedStorageTest.java	Tue Oct 22 18:20:04 2013 +0200
+++ b/storage/core/src/test/java/com/redhat/thermostat/storage/core/QueuedStorageTest.java	Mon Oct 28 15:30:44 2013 +0100
@@ -196,6 +196,8 @@
             }
         }
     }
+    
+    private static final Category<FooPojo> TEST_CATEGORY = new Category<>("foo-table", FooPojo.class);
 
     private QueuedStorage queuedStorage;
     private Storage delegateStorage;
@@ -272,9 +274,9 @@
         PreparedStatement<Pojo> statement = (PreparedStatement<Pojo>)mock(PreparedStatement.class);
         when(delegateStorage.prepareStatement(anyStatementDescriptor())).thenReturn(statement);
         
-        @SuppressWarnings("unchecked")
-        StatementDescriptor<Pojo> desc = mock(StatementDescriptor.class);
-        PreparedStatement<Pojo> decorated = queuedStorage.prepareStatement(desc);
+        
+        StatementDescriptor<FooPojo> desc = new StatementDescriptor<>(TEST_CATEGORY, "QUERY foo-table");
+        PreparedStatement<FooPojo> decorated = queuedStorage.prepareStatement(desc);
         assertNotNull(decorated);
         assertTrue(decorated instanceof QueuedPreparedStatement);
         assertNotSame(decorated, statement);
@@ -302,9 +304,8 @@
         Mockito.doThrow(StatementExecutionException.class).when(statement).execute();
         when(delegateStorage.prepareStatement(anyStatementDescriptor())).thenReturn(statement);
         
-        @SuppressWarnings("unchecked")
-        StatementDescriptor<Pojo> desc = mock(StatementDescriptor.class);
-        PreparedStatement<Pojo> decorated = queuedStorage.prepareStatement(desc);
+        StatementDescriptor<FooPojo> desc = new StatementDescriptor<>(TEST_CATEGORY, "QUERY foo-table");
+        PreparedStatement<FooPojo> decorated = queuedStorage.prepareStatement(desc);
         assertNotNull(decorated);
         assertTrue(decorated instanceof QueuedPreparedStatement);
         assertNotSame(decorated, statement);
@@ -331,8 +332,7 @@
         
         Mockito.doThrow(DescriptorParsingException.class).when(delegateStorage).prepareStatement(anyStatementDescriptor());
         
-        @SuppressWarnings("unchecked")
-        StatementDescriptor<Pojo> desc = mock(StatementDescriptor.class);
+        StatementDescriptor<FooPojo> desc = new StatementDescriptor<>(TEST_CATEGORY, "QUERY foo-table");
         
         try {
             queuedStorage.prepareStatement(desc);
@@ -458,5 +458,9 @@
         }
         
     }
+    
+    private static class FooPojo implements Pojo {
+       // Dummy class for testing 
+    }
 }
 
--- a/storage/core/src/test/java/com/redhat/thermostat/storage/internal/AdaptedCategoryTest.java	Tue Oct 22 18:20:04 2013 +0200
+++ b/storage/core/src/test/java/com/redhat/thermostat/storage/internal/AdaptedCategoryTest.java	Mon Oct 28 15:30:44 2013 +0100
@@ -39,12 +39,13 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import org.junit.Test;
 
+import com.redhat.thermostat.storage.core.Key;
 import com.redhat.thermostat.storage.dao.AgentInfoDAO;
 import com.redhat.thermostat.storage.dao.VmInfoDAO;
-import com.redhat.thermostat.storage.internal.AdaptedCategory;
 import com.redhat.thermostat.storage.model.AgentInformation;
 import com.redhat.thermostat.storage.model.AggregateCount;
 import com.redhat.thermostat.storage.model.AggregateResult;
@@ -63,6 +64,13 @@
     }
     
     @Test
+    public void testHashCode() {
+        AdaptedCategory<AggregateCount, AgentInformation> cat = new AdaptedCategory<>(AgentInfoDAO.CATEGORY, AggregateCount.class);
+        assertTrue("Adapted and original must have different hash code",
+                cat.hashCode() != AgentInfoDAO.CATEGORY.hashCode());
+    }
+    
+    @Test
     public void getDataClass() {
         AdaptedCategory<AggregateCount, AgentInformation> cat = new AdaptedCategory<>(AgentInfoDAO.CATEGORY, AggregateCount.class);
         assertEquals(AggregateCount.class, cat.getDataClass());
@@ -70,6 +78,17 @@
     }
     
     @Test
+    public void keysAreImmutable() {
+        AdaptedCategory<AggregateCount, AgentInformation> cat = new AdaptedCategory<>(AgentInfoDAO.CATEGORY, AggregateCount.class);
+        try {
+            cat.getKeys().add(new Key<>("foo"));
+            fail("keys need to be immutable");
+        } catch (UnsupportedOperationException e) {
+            // pass
+        }
+    }
+    
+    @Test
     public void adaptNonAggregateDataClass() {
         try {
             new AdaptedCategory<>(AgentInfoDAO.CATEGORY, VmInfo.class);
--- a/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/PreparedStatementImplTest.java	Tue Oct 22 18:20:04 2013 +0200
+++ b/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/PreparedStatementImplTest.java	Mon Oct 28 15:30:44 2013 +0100
@@ -45,10 +45,8 @@
 
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 import org.junit.Test;
 
@@ -73,7 +71,19 @@
 import com.redhat.thermostat.storage.query.LiteralExpression;
 
 public class PreparedStatementImplTest {
-
+    
+    private static int counter = 0;
+    
+    /*
+     * Category names need to be unique. In order to prevent IllegalStateExceptions
+     * create a new category name each time this is called.
+     */
+    private synchronized String getNextCategoryName() {
+        String name = "foo-table-" + counter;
+        counter++;
+        return name;
+    }
+    
     @Test
     public void failToSetIndexOutOfBounds() {
         PreparedStatementImpl<?> preparedStatement = new PreparedStatementImpl<>(2);
@@ -107,18 +117,14 @@
     
     @Test
     public void canDoParsingPatchingAndExecutionQuery() throws Exception {
-        String queryString = "QUERY foo WHERE 'a' = ?s";
-        @SuppressWarnings("unchecked")
-        StatementDescriptor<Pojo> desc = (StatementDescriptor<Pojo>) mock(StatementDescriptor.class);
-        when(desc.getDescriptor()).thenReturn(queryString);
-        @SuppressWarnings("unchecked")
-        Category<Pojo> mockCategory = (Category<Pojo>) mock(Category.class);
-        when(desc.getCategory()).thenReturn(mockCategory);
-        when(mockCategory.getName()).thenReturn("foo");
+        String categoryName = getNextCategoryName();
+        String queryString = "QUERY " + categoryName + " WHERE 'a' = ?s";
+        Category<FooPojo> fooCategory = new Category<>(categoryName, FooPojo.class, new Key<String>("a"));
+        StatementDescriptor<FooPojo> desc = new StatementDescriptor<>(fooCategory, queryString);
         BackingStorage storage = mock(BackingStorage.class);
-        StubQuery stmt = new StubQuery();
-        when(storage.createQuery(mockCategory)).thenReturn(stmt);
-        PreparedStatementImpl<Pojo> preparedStatement = new PreparedStatementImpl<>(storage, desc);
+        StubQuery<FooPojo> stmt = new StubQuery<>();
+        when(storage.createQuery(fooCategory)).thenReturn(stmt);
+        PreparedStatementImpl<FooPojo> preparedStatement = new PreparedStatementImpl<>(storage, desc);
         preparedStatement.setString(0, "foo");
         preparedStatement.executeQuery();
         assertTrue(stmt.called);
@@ -131,21 +137,13 @@
     
     @Test
     public void canDoParsingPatchingAndExecutionForAdd() throws Exception {
-        String addString = "ADD foo-table SET 'foo' = ?s";
-        @SuppressWarnings("unchecked")
-        StatementDescriptor<FooPojo> desc = (StatementDescriptor<FooPojo>) mock(StatementDescriptor.class);
-        when(desc.getDescriptor()).thenReturn(addString);
-        @SuppressWarnings("unchecked")
-        Category<FooPojo> mockCategory = (Category<FooPojo>) mock(Category.class);
-        when(desc.getCategory()).thenReturn(mockCategory);
-        Set<Key<?>> categoryKeys = new HashSet<>();
-        categoryKeys.add(new Key<>("foo"));
-        when(mockCategory.getKeys()).thenReturn(categoryKeys);
-        when(mockCategory.getDataClass()).thenReturn(FooPojo.class);
-        when(mockCategory.getName()).thenReturn("foo-table");
+        String categoryName = getNextCategoryName();
+        String addString = "ADD " + categoryName +" SET 'foo' = ?s";
+        Category<FooPojo> fooCategory = new Category<>(categoryName, FooPojo.class, new Key<String>("foo"));
+        StatementDescriptor<FooPojo> desc = new StatementDescriptor<>(fooCategory, addString);
         BackingStorage storage = mock(BackingStorage.class);
         TestAdd<FooPojo> add = new TestAdd<>();
-        when(storage.createAdd(mockCategory)).thenReturn(add);
+        when(storage.createAdd(fooCategory)).thenReturn(add);
         PreparedStatement<FooPojo> preparedStatement = new PreparedStatementImpl<FooPojo>(storage, desc);
         preparedStatement.setString(0, "foo-val");
         assertFalse(add.executed);
@@ -163,21 +161,13 @@
     
     @Test
     public void canDoParsingPatchingAndExecutionForAddInvolvingFancyPojo() throws Exception {
-        String addString = "ADD foo-table SET 'fancyFoo' = ?p[";
-        @SuppressWarnings("unchecked")
-        StatementDescriptor<FancyFoo> desc = (StatementDescriptor<FancyFoo>) mock(StatementDescriptor.class);
-        when(desc.getDescriptor()).thenReturn(addString);
-        @SuppressWarnings("unchecked")
-        Category<FancyFoo> mockCategory = (Category<FancyFoo>) mock(Category.class);
-        when(desc.getCategory()).thenReturn(mockCategory);
-        Set<Key<?>> categoryKeys = new HashSet<>();
-        categoryKeys.add(new Key<>("fancyFoo"));
-        when(mockCategory.getKeys()).thenReturn(categoryKeys);
-        when(mockCategory.getDataClass()).thenReturn(FancyFoo.class);
-        when(mockCategory.getName()).thenReturn("foo-table");
+        String categoryName = getNextCategoryName();
+        String addString = "ADD " + categoryName + " SET 'fancyFoo' = ?p[";
+        Category<FancyFoo> fooCategory = new Category<>(categoryName, FancyFoo.class, new Key<String>("fancyFoo"));
+        StatementDescriptor<FancyFoo> desc = new StatementDescriptor<>(fooCategory, addString);
         BackingStorage storage = mock(BackingStorage.class);
         TestAdd<FancyFoo> add = new TestAdd<>();
-        when(storage.createAdd(mockCategory)).thenReturn(add);
+        when(storage.createAdd(fooCategory)).thenReturn(add);
         PreparedStatement<FancyFoo> preparedStatement = new PreparedStatementImpl<FancyFoo>(storage, desc);
         FooPojo one = new FooPojo();
         one.setFoo("one");
@@ -207,21 +197,13 @@
     
     @Test
     public void canDoParsingPatchingAndExecutionForUpdate() throws Exception {
-        String addString = "UPDATE foo-table SET 'foo' = ?s WHERE 'foo' = ?s";
-        @SuppressWarnings("unchecked")
-        StatementDescriptor<FooPojo> desc = (StatementDescriptor<FooPojo>) mock(StatementDescriptor.class);
-        when(desc.getDescriptor()).thenReturn(addString);
-        @SuppressWarnings("unchecked")
-        Category<FooPojo> mockCategory = (Category<FooPojo>) mock(Category.class);
-        Set<Key<?>> categoryKeys = new HashSet<>();
-        categoryKeys.add(new Key<>("foo"));
-        when(mockCategory.getKeys()).thenReturn(categoryKeys);
-        when(desc.getCategory()).thenReturn(mockCategory);
-        when(mockCategory.getDataClass()).thenReturn(FooPojo.class);
-        when(mockCategory.getName()).thenReturn("foo-table");
+        String categoryName = getNextCategoryName();
+        String addString = "UPDATE " + categoryName + " SET 'foo' = ?s WHERE 'foo' = ?s";
+        Category<FooPojo> fooCategory = new Category<>(categoryName, FooPojo.class, new Key<String>("foo"));
+        StatementDescriptor<FooPojo> desc = new StatementDescriptor<>(fooCategory, addString);
         BackingStorage storage = mock(BackingStorage.class);
         TestUpdate update = new TestUpdate();
-        when(storage.createUpdate(mockCategory)).thenReturn(update);
+        when(storage.createUpdate(fooCategory)).thenReturn(update);
         PreparedStatement<FooPojo> preparedStatement = new PreparedStatementImpl<FooPojo>(storage, desc);
         preparedStatement.setString(0, "foo-val");
         preparedStatement.setString(1, "nice");
@@ -246,21 +228,13 @@
     
     @Test
     public void canDoParsingPatchingAndExecutionForReplace() throws Exception {
-        String addString = "REPLACE foo-table SET 'foo' = ?s WHERE 'foo' = ?s";
-        @SuppressWarnings("unchecked")
-        StatementDescriptor<FooPojo> desc = (StatementDescriptor<FooPojo>) mock(StatementDescriptor.class);
-        when(desc.getDescriptor()).thenReturn(addString);
-        @SuppressWarnings("unchecked")
-        Category<FooPojo> mockCategory = (Category<FooPojo>) mock(Category.class);
-        Set<Key<?>> categoryKeys = new HashSet<>();
-        categoryKeys.add(new Key<>("foo"));
-        when(mockCategory.getKeys()).thenReturn(categoryKeys);
-        when(desc.getCategory()).thenReturn(mockCategory);
-        when(mockCategory.getDataClass()).thenReturn(FooPojo.class);
-        when(mockCategory.getName()).thenReturn("foo-table");
+        String categoryName = getNextCategoryName();
+        String addString = "REPLACE " + categoryName + " SET 'foo' = ?s WHERE 'foo' = ?s";
+        Category<FooPojo> fooCategory = new Category<>(categoryName, FooPojo.class, new Key<String>("foo"));
+        StatementDescriptor<FooPojo> desc = new StatementDescriptor<>(fooCategory, addString);
         BackingStorage storage = mock(BackingStorage.class);
-        TestReplace replace = new TestReplace();
-        when(storage.createReplace(mockCategory)).thenReturn(replace);
+        TestReplace<FooPojo> replace = new TestReplace<>();
+        when(storage.createReplace(fooCategory)).thenReturn(replace);
         PreparedStatement<FooPojo> preparedStatement = new PreparedStatementImpl<FooPojo>(storage, desc);
         preparedStatement.setString(0, "foo-val");
         preparedStatement.setString(1, "bar");
@@ -283,18 +257,13 @@
     
     @Test
     public void canDoParsingPatchingAndExecutionForRemove() throws Exception {
-        String addString = "REMOVE foo-table WHERE 'fooRem' = ?s";
-        @SuppressWarnings("unchecked")
-        StatementDescriptor<FooPojo> desc = (StatementDescriptor<FooPojo>) mock(StatementDescriptor.class);
-        when(desc.getDescriptor()).thenReturn(addString);
-        @SuppressWarnings("unchecked")
-        Category<FooPojo> mockCategory = (Category<FooPojo>) mock(Category.class);
-        when(desc.getCategory()).thenReturn(mockCategory);
-        when(mockCategory.getDataClass()).thenReturn(FooPojo.class);
-        when(mockCategory.getName()).thenReturn("foo-table");
+        String categoryName = getNextCategoryName();
+        String addString = "REMOVE " + categoryName + " WHERE 'fooRem' = ?s";
+        Category<FooPojo> fooCategory = new Category<>(categoryName, FooPojo.class, new Key<String>("foo"));
+        StatementDescriptor<FooPojo> desc = new StatementDescriptor<>(fooCategory, addString);
         BackingStorage storage = mock(BackingStorage.class);
-        TestRemove remove = new TestRemove();
-        when(storage.createRemove(mockCategory)).thenReturn(remove);
+        TestRemove<FooPojo> remove = new TestRemove<>();
+        when(storage.createRemove(fooCategory)).thenReturn(remove);
         PreparedStatement<FooPojo> preparedStatement = new PreparedStatementImpl<FooPojo>(storage, desc);
         preparedStatement.setString(0, "bar");
         assertFalse(remove.executed);
@@ -314,18 +283,14 @@
     
     @Test
     public void failExecutionWithWronglyTypedParams() throws Exception {
-        String queryString = "QUERY foo WHERE 'a' = ?b";
-        @SuppressWarnings("unchecked")
-        StatementDescriptor<Pojo> desc = (StatementDescriptor<Pojo>) mock(StatementDescriptor.class);
-        when(desc.getDescriptor()).thenReturn(queryString);
-        @SuppressWarnings("unchecked")
-        Category<Pojo> mockCategory = (Category<Pojo>) mock(Category.class);
-        when(desc.getCategory()).thenReturn(mockCategory);
-        when(mockCategory.getName()).thenReturn("foo");
+        String categoryName = getNextCategoryName();
+        String queryString = "QUERY " + categoryName + " WHERE 'a' = ?b";
+        Category<FooPojo> fooCategory = new Category<>(categoryName, FooPojo.class, new Key<String>("a"));
+        StatementDescriptor<FooPojo> desc = new StatementDescriptor<>(fooCategory, queryString);
         BackingStorage storage = mock(BackingStorage.class);
-        StubQuery stmt = new StubQuery();
-        when(storage.createQuery(mockCategory)).thenReturn(stmt);
-        PreparedStatementImpl<Pojo> preparedStatement = new PreparedStatementImpl<>(storage, desc);
+        StubQuery<FooPojo> stmt = new StubQuery<>();
+        when(storage.createQuery(fooCategory)).thenReturn(stmt);
+        PreparedStatementImpl<FooPojo> preparedStatement = new PreparedStatementImpl<>(storage, desc);
         preparedStatement.setString(0, "foo");
         try {
             preparedStatement.executeQuery();
@@ -360,7 +325,7 @@
 
     }
     
-    private static class TestReplace implements Replace<FooPojo> {
+    private static class TestReplace<T extends Pojo> implements Replace<T> {
 
         private Map<String, Object> values = new HashMap<>();
         private boolean executed = false;
@@ -383,7 +348,7 @@
         }
 
         @Override
-        public Statement<FooPojo> getRawDuplicate() {
+        public Statement<T> getRawDuplicate() {
             // we don't duplicate for this test
             return this;
         }
@@ -421,7 +386,7 @@
         
     }
     
-    private static class TestRemove implements Remove<FooPojo> {
+    private static class TestRemove<T extends Pojo> implements Remove<T> {
 
         private Expression where;
         private boolean executed = false;
@@ -438,7 +403,7 @@
         }
 
         @Override
-        public Statement<FooPojo> getRawDuplicate() {
+        public Statement<T> getRawDuplicate() {
             // we don't duplicate for this test
             return this;
         }
@@ -472,7 +437,7 @@
         
     }
     
-    private static class StubQuery implements Query<Pojo> {
+    private static class StubQuery<T extends Pojo> implements Query<T> {
 
         private Expression expr;
         private boolean called = false;
@@ -493,7 +458,7 @@
         }
 
         @Override
-        public Cursor<Pojo> execute() {
+        public Cursor<T> execute() {
             called = true;
             return null;
         }
@@ -505,7 +470,7 @@
         }
 
         @Override
-        public Statement<Pojo> getRawDuplicate() {
+        public Statement<T> getRawDuplicate() {
             // For this test, we don't duplicate
             return this;
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/common/src/test/java/com/redhat/thermostat/web/common/CategorySerializationTest.java	Mon Oct 28 15:30:44 2013 +0100
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2012, 2013 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.web.common;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.redhat.thermostat.storage.core.Category;
+import com.redhat.thermostat.storage.core.Key;
+import com.redhat.thermostat.storage.model.Pojo;
+
+public class CategorySerializationTest {
+    
+    private static class TestObj implements Pojo {
+        // Dummy class for testing.
+    }
+
+    private Gson gson;
+    
+    @Before
+    public void setup() {
+        gson = new GsonBuilder().create();
+    }
+    
+    @Test
+    public void canSerializeDeserializeCategory() {
+        Key<Boolean> barKey = new Key<>("bar-key");
+        Category<TestObj> cat = new Category<>("foo-category", TestObj.class, barKey);
+        String str = gson.toJson(cat, Category.class);
+        @SuppressWarnings("unchecked")
+        Category<TestObj> cat2 = (Category<TestObj>)gson.fromJson(str, Category.class);
+        assertNotSame(cat, cat2);
+        assertTrue(cat.equals(cat2));
+        assertEquals(cat.hashCode(), cat2.hashCode());
+        try {
+            cat2.getKeys().add(new Key<>("testme"));
+            fail("keys must be immutable after deserialization");
+        } catch (UnsupportedOperationException e) {
+            // pass
+        }
+        assertEquals(TestObj.class, cat2.getDataClass());
+        assertEquals("foo-category", cat2.getName());
+        assertEquals(barKey, cat2.getKey("bar-key"));
+    }
+}
--- a/web/server/src/test/java/com/redhat/thermostat/web/server/auth/AgentIdFilterTest.java	Tue Oct 22 18:20:04 2013 +0200
+++ b/web/server/src/test/java/com/redhat/thermostat/web/server/auth/AgentIdFilterTest.java	Mon Oct 28 15:30:44 2013 +0100
@@ -41,9 +41,6 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.mockito.Matchers.eq;
 
 import java.util.HashSet;
 import java.util.Set;
@@ -55,6 +52,7 @@
 import com.redhat.thermostat.storage.core.Key;
 import com.redhat.thermostat.storage.core.StatementDescriptor;
 import com.redhat.thermostat.storage.core.auth.DescriptorMetadata;
+import com.redhat.thermostat.storage.model.Pojo;
 import com.redhat.thermostat.storage.query.BinaryLogicalExpression;
 import com.redhat.thermostat.storage.query.BinarySetMembershipExpression;
 import com.redhat.thermostat.storage.query.Expression;
@@ -63,6 +61,21 @@
 
 public class AgentIdFilterTest {
     
+    private static class FooPojo implements Pojo {
+        // Dummy class for testing
+    }
+    
+    private static final Category<FooPojo> TEST_NON_NULL_CATEGORY = new Category<>("foo-agentid-filter-test", FooPojo.class, Key.AGENT_ID);
+    private static final Category<FooPojo> TEST_NULL_CATEGORY = new Category<>("foo-agentid-filter-test-null", FooPojo.class);
+    /**
+     * A query descriptor which will return a non-null Key for the "vmId" name.
+     */
+    private static final StatementDescriptor<FooPojo> TEST_DESC_NON_NULL_AGENT_ID = new StatementDescriptor<>(TEST_NON_NULL_CATEGORY, "QUERY foo");
+    /**
+     * A query descriptor which will return a null Key for the "vmId" name.
+     */
+    private static final StatementDescriptor<FooPojo> TEST_DESC_NULL_AGENT_ID = new StatementDescriptor<>(TEST_NULL_CATEGORY, "QUERY foo-null");
+    
     @Test
     public void testReadAll() {
         Set<BasicRole> roles = new HashSet<>();
@@ -89,7 +102,6 @@
         assertEquals(parentExpression, result.getFilterExpression());
     }
     
-    @SuppressWarnings({"unchecked", "rawtypes"})
     @Test
     public void addsAgentIdInQuery() {
         String agentId = UUID.randomUUID().toString();
@@ -97,14 +109,9 @@
         RolePrincipal agent1Role = new RolePrincipal(AgentIdFilter.AGENTS_BY_AGENT_ID_GRANT_ROLE_PREFIX + agentId);
         roles.add(agent1Role);
         DescriptorMetadata metadata = new DescriptorMetadata();
-        StatementDescriptor desc = mock(StatementDescriptor.class);
-        Category category = mock(Category.class);
-        Key<?> mockKey = mock(Key.class);
-        // returning non-null will work
-        when(category.getKey(eq(Key.AGENT_ID.getName()))).thenReturn(mockKey);
-        when(desc.getCategory()).thenReturn(category);
-        AgentIdFilter<?> filter = new AgentIdFilter<>(roles);
-        FilterResult result = filter.applyFilter(desc, metadata, null);
+        AgentIdFilter<FooPojo> filter = new AgentIdFilter<>(roles);
+        // returning non-null agent id key will work
+        FilterResult result = filter.applyFilter(TEST_DESC_NON_NULL_AGENT_ID, metadata, null);
         assertEquals(ResultType.QUERY_EXPRESSION, result.getType());
         assertNotNull(result.getFilterExpression());
         Expression actual = result.getFilterExpression();
@@ -115,7 +122,6 @@
         assertEquals(expected, actual);
     }
     
-    @SuppressWarnings({ "rawtypes", "unchecked" })
     @Test
     public void addsAgentIdInQueryToParentExpression() {
         String agentId = UUID.randomUUID().toString();
@@ -123,16 +129,10 @@
         RolePrincipal agent1Role = new RolePrincipal(AgentIdFilter.AGENTS_BY_AGENT_ID_GRANT_ROLE_PREFIX + agentId);
         roles.add(agent1Role);
         DescriptorMetadata metadata = new DescriptorMetadata();
-        StatementDescriptor desc = mock(StatementDescriptor.class);
-        Category category = mock(Category.class);
-        Key<?> mockKey = mock(Key.class);
-        // returning non-null will work
-        when(category.getKey(eq(Key.AGENT_ID.getName()))).thenReturn(mockKey);
-        when(desc.getCategory()).thenReturn(category);
-        AgentIdFilter<?> filter = new AgentIdFilter<>(roles);
+        AgentIdFilter<FooPojo> filter = new AgentIdFilter<>(roles);
         ExpressionFactory factory = new ExpressionFactory();
         Expression parentExpression = factory.equalTo(Key.AGENT_ID, "testKey");
-        FilterResult result = filter.applyFilter(desc, metadata, parentExpression);
+        FilterResult result = filter.applyFilter(TEST_DESC_NON_NULL_AGENT_ID, metadata, parentExpression);
         assertEquals(ResultType.QUERY_EXPRESSION, result.getType());
         assertNotNull(result.getFilterExpression());
         Expression actual = result.getFilterExpression();
@@ -144,7 +144,6 @@
         assertEquals(expected, actual);
     }
     
-    @SuppressWarnings({ "rawtypes", "unchecked" })
     @Test
     public void addsAgentIdInQuery2() {
         String agentId = UUID.randomUUID().toString();
@@ -155,14 +154,8 @@
         roles.add(agent1Role);
         roles.add(agent2Role);
         DescriptorMetadata metadata = new DescriptorMetadata();
-        StatementDescriptor desc = mock(StatementDescriptor.class);
-        Category category = mock(Category.class);
-        Key<?> mockKey = mock(Key.class);
-        // returning non-null will work
-        when(category.getKey(eq(Key.AGENT_ID.getName()))).thenReturn(mockKey);
-        when(desc.getCategory()).thenReturn(category);
-        AgentIdFilter<?> filter = new AgentIdFilter<>(roles);
-        FilterResult result = filter.applyFilter(desc, metadata, null);
+        AgentIdFilter<FooPojo> filter = new AgentIdFilter<>(roles);
+        FilterResult result = filter.applyFilter(TEST_DESC_NON_NULL_AGENT_ID, metadata, null);
         assertEquals(ResultType.QUERY_EXPRESSION, result.getType());
         assertNotNull(result.getFilterExpression());
         Expression actual = result.getFilterExpression();
@@ -174,7 +167,6 @@
         assertEquals(expected, actual);
     }
     
-    @SuppressWarnings({ "rawtypes", "unchecked" })
     @Test
     public void returnsEmptyIfAgentIdDoesNotMatch() {
         String agentId = UUID.randomUUID().toString();
@@ -186,52 +178,36 @@
         assertFalse(agentId.equals(wrongAgentId));
         
         DescriptorMetadata metadata = new DescriptorMetadata(wrongAgentId);
-        StatementDescriptor desc = mock(StatementDescriptor.class);
-        Category category = mock(Category.class);
-        Key<?> mockKey = mock(Key.class);
-        // returning non-null will work
-        when(category.getKey(eq(Key.AGENT_ID.getName()))).thenReturn(mockKey);
-        when(desc.getCategory()).thenReturn(category);
         assertTrue(metadata.hasAgentId());
-        AgentIdFilter<?> filter = new AgentIdFilter<>(roles);
-        FilterResult result = filter.applyFilter(desc, metadata, null);
+        AgentIdFilter<FooPojo> filter = new AgentIdFilter<>(roles);
+        FilterResult result = filter.applyFilter(TEST_DESC_NON_NULL_AGENT_ID, metadata, null);
         assertEquals(ResultType.EMPTY, result.getType());
         assertNull(result.getFilterExpression());
     }
     
-    @SuppressWarnings({ "rawtypes", "unchecked" })
     @Test
     public void returnsAllForUnrelatedQuery() {
         Set<BasicRole> roles = new HashSet<>();
         
         DescriptorMetadata metadata = new DescriptorMetadata();
-        StatementDescriptor desc = mock(StatementDescriptor.class);
-        Category category = mock(Category.class);
+        assertFalse(metadata.hasAgentId());
         // want for the agent id key to not be present in category
-        when(category.getKey(eq(Key.AGENT_ID.getName()))).thenReturn(null);
-        when(desc.getCategory()).thenReturn(category);
-        assertFalse(metadata.hasAgentId());
-        AgentIdFilter<?> filter = new AgentIdFilter<>(roles);
-        FilterResult result = filter.applyFilter(desc, metadata, null);
+        AgentIdFilter<FooPojo> filter = new AgentIdFilter<>(roles);
+        FilterResult result = filter.applyFilter(TEST_DESC_NULL_AGENT_ID, metadata, null);
         assertEquals(ResultType.ALL, result.getType());
         assertNull(result.getFilterExpression());
     }
     
-    @SuppressWarnings({ "unchecked", "rawtypes" })
     @Test
     public void returnsParentExpressionForUnrelatedQuery() {
         Set<BasicRole> roles = new HashSet<>();
         
         Expression parentExpression = new ExpressionFactory().equalTo(Key.AGENT_ID, "testKey");
         DescriptorMetadata metadata = new DescriptorMetadata();
-        StatementDescriptor desc = mock(StatementDescriptor.class);
-        Category category = mock(Category.class);
+        assertFalse(metadata.hasAgentId());
+        AgentIdFilter<FooPojo> filter = new AgentIdFilter<>(roles);
         // want for the agent id key to not be present in category
-        when(category.getKey(eq(Key.AGENT_ID.getName()))).thenReturn(null);
-        when(desc.getCategory()).thenReturn(category);
-        assertFalse(metadata.hasAgentId());
-        AgentIdFilter<?> filter = new AgentIdFilter<>(roles);
-        FilterResult result = filter.applyFilter(desc, metadata, parentExpression);
+        FilterResult result = filter.applyFilter(TEST_DESC_NULL_AGENT_ID, metadata, parentExpression);
         assertEquals(ResultType.QUERY_EXPRESSION, result.getType());
         assertNotNull(result.getFilterExpression());
         assertEquals(parentExpression, result.getFilterExpression());
--- a/web/server/src/test/java/com/redhat/thermostat/web/server/auth/HostnameFilterTest.java	Tue Oct 22 18:20:04 2013 +0200
+++ b/web/server/src/test/java/com/redhat/thermostat/web/server/auth/HostnameFilterTest.java	Mon Oct 28 15:30:44 2013 +0100
@@ -40,8 +40,6 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
 import java.util.HashSet;
 import java.util.Set;
@@ -97,9 +95,7 @@
         roles.add(hostnameRole);
         
         DescriptorMetadata metadata = new DescriptorMetadata();
-        @SuppressWarnings("unchecked")
-        StatementDescriptor<HostInfo> desc = mock(StatementDescriptor.class);
-        when(desc.getCategory()).thenReturn(HostInfoDAO.hostInfoCategory);
+        StatementDescriptor<HostInfo> desc = new StatementDescriptor<>(HostInfoDAO.hostInfoCategory, "QUERY " + HostInfoDAO.hostInfoCategory.getName());
         
         Set<String> hostnames = new HashSet<>();
         hostnames.add(testHostname);
@@ -121,9 +117,7 @@
         roles.add(hostnameRole);
         
         DescriptorMetadata metadata = new DescriptorMetadata();
-        @SuppressWarnings("unchecked")
-        StatementDescriptor<HostInfo> desc = mock(StatementDescriptor.class);
-        when(desc.getCategory()).thenReturn(HostInfoDAO.hostInfoCategory);
+        StatementDescriptor<HostInfo> desc = new StatementDescriptor<>(HostInfoDAO.hostInfoCategory, "QUERY " + HostInfoDAO.hostInfoCategory.getName());
         
         Set<String> hostnames = new HashSet<>();
         hostnames.add(testHostname);
@@ -146,9 +140,7 @@
         
         DescriptorMetadata metadata = new DescriptorMetadata();
 
-        @SuppressWarnings("unchecked")
-        StatementDescriptor<AgentInformation> desc = mock(StatementDescriptor.class);
-        when(desc.getCategory()).thenReturn(AgentInfoDAO.CATEGORY);
+        StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, "QUERY " + AgentInfoDAO.CATEGORY.getName());
         
         HostnameFilter<AgentInformation> filter = new HostnameFilter<>(roles);
         FilterResult result = filter.applyFilter(desc, metadata, null);
@@ -161,9 +153,7 @@
         Set<BasicRole> roles = new HashSet<>();
         
         DescriptorMetadata metadata = new DescriptorMetadata();
-        @SuppressWarnings("unchecked")
-        StatementDescriptor<AgentInformation> desc = mock(StatementDescriptor.class);
-        when(desc.getCategory()).thenReturn(AgentInfoDAO.CATEGORY);
+        StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, "QUERY " + AgentInfoDAO.CATEGORY.getName());
         
         ExpressionFactory factory = new ExpressionFactory();
         Expression parentExpression = factory.equalTo(Key.AGENT_ID, "testKey");
--- a/web/server/src/test/java/com/redhat/thermostat/web/server/auth/UserPrincipalTest.java	Tue Oct 22 18:20:04 2013 +0200
+++ b/web/server/src/test/java/com/redhat/thermostat/web/server/auth/UserPrincipalTest.java	Mon Oct 28 15:30:44 2013 +0100
@@ -42,9 +42,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
-import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
 import java.security.Principal;
 import java.util.HashSet;
@@ -59,12 +57,17 @@
 import com.redhat.thermostat.storage.dao.HostInfoDAO;
 import com.redhat.thermostat.storage.dao.VmInfoDAO;
 import com.redhat.thermostat.storage.model.HostInfo;
+import com.redhat.thermostat.storage.model.Pojo;
 import com.redhat.thermostat.storage.model.VmInfo;
 import com.redhat.thermostat.storage.query.Expression;
 import com.redhat.thermostat.storage.query.ExpressionFactory;
 import com.redhat.thermostat.web.server.auth.FilterResult.ResultType;
 
 public class UserPrincipalTest {
+    
+    private static class FooPojo implements Pojo {
+        // empty dummy pojo.
+    }
 
     @Test(expected = NullPointerException.class)
     public void testConstructor() {
@@ -148,9 +151,7 @@
         assertFalse(roles.contains(VmUsernameFilter.GRANT_VMS_USERNAME_READ_ALL));
         SimplePrincipal testMe = new SimplePrincipal("test me");
         testMe.setRoles(roles);
-        @SuppressWarnings("unchecked")
-        StatementDescriptor<VmInfo> desc = mock(StatementDescriptor.class);
-        when(desc.getCategory()).thenReturn(VmInfoDAO.vmInfoCategory);
+        StatementDescriptor<VmInfo> desc = new StatementDescriptor<>(VmInfoDAO.vmInfoCategory, "QUERY " + VmInfoDAO.vmInfoCategory.getName());
         
         // fake a query for a category with agentId attributes and vmId
         // attributes present, but no specific agentId/vmId present.
@@ -178,7 +179,6 @@
         assertEquals(expected, actual);
     }
     
-    @SuppressWarnings({ "rawtypes", "unchecked" })
     @Test
     public void testEntireFilterChainSpecificAgentIdVmId() {
         String agentId = "someAgentID";
@@ -198,15 +198,13 @@
         assertTrue(roles.contains(VmUsernameFilter.GRANT_VMS_USERNAME_READ_ALL));
         SimplePrincipal testMe = new SimplePrincipal("test me");
         testMe.setRoles(roles);
-        StatementDescriptor desc = mock(StatementDescriptor.class);
-        Category mockCategory = mock(Category.class);
-        Key<?> agentKey = mock(Key.class);
-        // want for the agent id key to be present in category
-        when(mockCategory.getKey(eq(Key.AGENT_ID.getName()))).thenReturn(agentKey);
-        Key<?> vmKey = mock(Key.class);
-        // want for the vm id key to be present in category
-        when(mockCategory.getKey(eq(Key.VM_ID.getName()))).thenReturn(vmKey);
-        when(desc.getCategory()).thenReturn(mockCategory);
+        
+        // want for the agent/vm id key to be present in category
+        Key<?>[] keys = new Key[] {
+             Key.AGENT_ID, Key.VM_ID
+        };
+        Category<FooPojo> agentAndVmIdCat = new Category<>("agentAndVmIdCat", FooPojo.class, keys);
+        StatementDescriptor<FooPojo> desc = new StatementDescriptor<>(agentAndVmIdCat, "QUERY agentAndVmIdCat");
         
         // fake a query for a category with agentId attributes and vmId
         // attributes present and also specific agentId/vmId present.
@@ -222,7 +220,6 @@
         assertNull(result.getFilterExpression());
     }
     
-    @SuppressWarnings({ "rawtypes", "unchecked" })
     @Test
     public void testEntireFilterChainSpecificAgentIdVmIdPlusHostname() {
         String agentId = "someAgentID";
@@ -244,15 +241,7 @@
         assertTrue(roles.contains(VmUsernameFilter.GRANT_VMS_USERNAME_READ_ALL));
         SimplePrincipal testMe = new SimplePrincipal("test me");
         testMe.setRoles(roles);
-        StatementDescriptor<HostInfo> desc = mock(StatementDescriptor.class);
-        Category mockCategory = mock(Category.class);
-        when(desc.getCategory()).thenReturn(HostInfoDAO.hostInfoCategory);
-        Key<?> agentKey = mock(Key.class);
-        // want for the agent id key to be present in category
-        when(mockCategory.getKey(eq(Key.AGENT_ID.getName()))).thenReturn(agentKey);
-        Key<?> vmKey = mock(Key.class);
-        // want for the vm id key to be present in category
-        when(mockCategory.getKey(eq(Key.VM_ID.getName()))).thenReturn(vmKey);
+        StatementDescriptor<HostInfo> desc = new StatementDescriptor<>(HostInfoDAO.hostInfoCategory, "QUERY " + HostInfoDAO.hostInfoCategory.getName());
         
         // fake a query for a category with agentId attributes and vmId
         // attributes present and also specific agentId/vmId present.
@@ -296,9 +285,7 @@
         assertFalse(roles.contains(VmUsernameFilter.GRANT_VMS_USERNAME_READ_ALL));
         SimplePrincipal testMe = new SimplePrincipal("test me");
         testMe.setRoles(roles);
-        @SuppressWarnings("unchecked")
-        StatementDescriptor<VmInfo> desc = mock(StatementDescriptor.class);
-        when(desc.getCategory()).thenReturn(VmInfoDAO.vmInfoCategory);
+        StatementDescriptor<VmInfo> desc = new StatementDescriptor<>(VmInfoDAO.vmInfoCategory, "QUERY " + VmInfoDAO.vmInfoCategory.getName());
         
         // fake a query for a category with agentId attributes and vmId
         // attributes present and also specific agentId/vmId present.
--- a/web/server/src/test/java/com/redhat/thermostat/web/server/auth/VmIdFilterTest.java	Tue Oct 22 18:20:04 2013 +0200
+++ b/web/server/src/test/java/com/redhat/thermostat/web/server/auth/VmIdFilterTest.java	Mon Oct 28 15:30:44 2013 +0100
@@ -41,9 +41,6 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
 import java.util.HashSet;
 import java.util.Set;
@@ -55,6 +52,7 @@
 import com.redhat.thermostat.storage.core.Key;
 import com.redhat.thermostat.storage.core.StatementDescriptor;
 import com.redhat.thermostat.storage.core.auth.DescriptorMetadata;
+import com.redhat.thermostat.storage.model.Pojo;
 import com.redhat.thermostat.storage.query.BinaryLogicalExpression;
 import com.redhat.thermostat.storage.query.BinarySetMembershipExpression;
 import com.redhat.thermostat.storage.query.Expression;
@@ -62,6 +60,21 @@
 import com.redhat.thermostat.web.server.auth.FilterResult.ResultType;
 
 public class VmIdFilterTest {
+    
+    private static class FooPojo implements Pojo {
+        // Dummy class for testing
+    }
+    
+    private static final Category<FooPojo> TEST_NON_NULL_CATEGORY = new Category<>("foo-vmid-filter-test", FooPojo.class, Key.VM_ID);
+    private static final Category<FooPojo> TEST_NULL_CATEGORY = new Category<>("foo-vmid-filter-test-null", FooPojo.class);
+    /**
+     * A query descriptor which will return a non-null Key for the "vmId" name.
+     */
+    private static final StatementDescriptor<FooPojo> TEST_DESC_NON_NULL_VM_ID = new StatementDescriptor<>(TEST_NON_NULL_CATEGORY, "QUERY foo");
+    /**
+     * A query descriptor which will return a null Key for the "vmId" name.
+     */
+    private static final StatementDescriptor<FooPojo> TEST_DESC_NULL_VM_ID = new StatementDescriptor<>(TEST_NULL_CATEGORY, "QUERY foo-null");
 
     @Test
     public void testReadAll() {
@@ -89,7 +102,6 @@
         assertEquals(parentExpression, result.getFilterExpression());
     }
     
-    @SuppressWarnings({ "rawtypes", "unchecked" })
     @Test
     public void addsVmIdInQuery() {
         String vmId = UUID.randomUUID().toString();
@@ -97,14 +109,8 @@
         RolePrincipal vmIdRole = new RolePrincipal(VmIdFilter.VMS_BY_VM_ID_GRANT_ROLE_PREFIX + vmId);
         roles.add(vmIdRole);
         DescriptorMetadata metadata = new DescriptorMetadata();
-        StatementDescriptor desc = mock(StatementDescriptor.class);
-        Category category = mock(Category.class);
-        Key<?> vmIdKey = mock(Key.class);
-        // any non-null key for vmId will do
-        when(category.getKey(eq(Key.VM_ID.getName()))).thenReturn(vmIdKey);
-        when(desc.getCategory()).thenReturn(category);
-        VmIdFilter<?> filter = new VmIdFilter<>(roles);
-        FilterResult result = filter.applyFilter(desc, metadata, null);
+        VmIdFilter<FooPojo> filter = new VmIdFilter<>(roles);
+        FilterResult result = filter.applyFilter(TEST_DESC_NON_NULL_VM_ID, metadata, null);
         assertEquals(ResultType.QUERY_EXPRESSION, result.getType());
         assertNotNull(result.getFilterExpression());
         Expression actual = result.getFilterExpression();
@@ -115,7 +121,6 @@
         assertEquals(expected, actual);
     }
     
-    @SuppressWarnings({ "rawtypes", "unchecked" })
     @Test
     public void addsVmIdInQueryAndParentExpression() {
         String vmId = UUID.randomUUID().toString();
@@ -123,16 +128,10 @@
         RolePrincipal vmIdRole = new RolePrincipal(VmIdFilter.VMS_BY_VM_ID_GRANT_ROLE_PREFIX + vmId);
         roles.add(vmIdRole);
         DescriptorMetadata metadata = new DescriptorMetadata();
-        StatementDescriptor desc = mock(StatementDescriptor.class);
-        Category category = mock(Category.class);
-        Key<?> vmIdKey = mock(Key.class);
-        // any non-null key for vmId will do
-        when(category.getKey(eq(Key.VM_ID.getName()))).thenReturn(vmIdKey);
-        when(desc.getCategory()).thenReturn(category);
-        VmIdFilter<?> filter = new VmIdFilter<>(roles);
+        VmIdFilter<FooPojo> filter = new VmIdFilter<>(roles);
         ExpressionFactory factory = new ExpressionFactory();
         Expression parentExpression = factory.equalTo(Key.AGENT_ID, "testKey");
-        FilterResult result = filter.applyFilter(desc, metadata, parentExpression);
+        FilterResult result = filter.applyFilter(TEST_DESC_NON_NULL_VM_ID, metadata, parentExpression);
         assertEquals(ResultType.QUERY_EXPRESSION, result.getType());
         assertNotNull(result.getFilterExpression());
         Expression actual = result.getFilterExpression();
@@ -145,7 +144,6 @@
         assertEquals(expected, actual);
     }
     
-    @SuppressWarnings({ "unchecked", "rawtypes" })
     @Test
     public void addsVmIdInQuery2() {
         String vmId = UUID.randomUUID().toString();
@@ -156,14 +154,8 @@
         roles.add(vm1Role);
         roles.add(vm2Role);
         DescriptorMetadata metadata = new DescriptorMetadata();
-        StatementDescriptor desc = mock(StatementDescriptor.class);
-        Category category = mock(Category.class);
-        Key<?> vmIdKey = mock(Key.class);
-        // any non-null key for vmId will do
-        when(category.getKey(eq(Key.VM_ID.getName()))).thenReturn(vmIdKey);
-        when(desc.getCategory()).thenReturn(category);
-        VmIdFilter<?> filter = new VmIdFilter<>(roles);
-        FilterResult result = filter.applyFilter(desc, metadata, null);
+        VmIdFilter<FooPojo> filter = new VmIdFilter<>(roles);
+        FilterResult result = filter.applyFilter(TEST_DESC_NON_NULL_VM_ID, metadata, null);
         assertEquals(ResultType.QUERY_EXPRESSION, result.getType());
         assertNotNull(result.getFilterExpression());
         Expression actual = result.getFilterExpression();
@@ -175,7 +167,6 @@
         assertEquals(expected, actual);
     }
     
-    @SuppressWarnings({ "unchecked", "rawtypes" })
     @Test
     public void returnsEmptyIfVmIdDoesNotMatch() {
         String vmId = UUID.randomUUID().toString();
@@ -187,39 +178,27 @@
         assertFalse(vmId.equals(wrongVmId));
         
         DescriptorMetadata metadata = new DescriptorMetadata(null, wrongVmId);
-        StatementDescriptor desc = mock(StatementDescriptor.class);
-        Category category = mock(Category.class);
-        Key<?> vmIdKey = mock(Key.class);
-        // any non-null key for vmId will do
-        when(category.getKey(eq(Key.VM_ID.getName()))).thenReturn(vmIdKey);
-        when(desc.getCategory()).thenReturn(category);
         assertTrue(metadata.hasVmId());
-        VmIdFilter<?> filter = new VmIdFilter<>(roles);
-        FilterResult result = filter.applyFilter(desc, metadata, null);
+        VmIdFilter<FooPojo> filter = new VmIdFilter<>(roles);
+        FilterResult result = filter.applyFilter(TEST_DESC_NON_NULL_VM_ID, metadata, null);
         assertEquals(ResultType.EMPTY, result.getType());
         assertNull(result.getFilterExpression());
     }
     
-    @SuppressWarnings({ "unchecked", "rawtypes" })
     @Test
     public void returnsAllForUnrelatedQuery() {
         Set<BasicRole> roles = new HashSet<>();
         
         DescriptorMetadata metadata = new DescriptorMetadata();
-        StatementDescriptor desc = mock(StatementDescriptor.class);
-        Category category = mock(Category.class);
-        // want to have a null retval of vmId
-        when(category.getKey(eq(Key.VM_ID.getName()))).thenReturn(null);
-        when(desc.getCategory()).thenReturn(category);
         assertFalse(metadata.hasAgentId());
         assertFalse(metadata.hasVmId());
-        VmIdFilter<?> filter = new VmIdFilter<>(roles);
-        FilterResult result = filter.applyFilter(desc, metadata, null);
+        VmIdFilter<FooPojo> filter = new VmIdFilter<>(roles);
+        // want to have a null retval of vmId
+        FilterResult result = filter.applyFilter(TEST_DESC_NULL_VM_ID, metadata, null);
         assertEquals(ResultType.ALL, result.getType());
         assertNull(result.getFilterExpression());
     }
     
-    @SuppressWarnings({ "rawtypes", "unchecked" })
     @Test
     public void returnsParentExpressionForUnrelatedQuery() {
         Set<BasicRole> roles = new HashSet<>();
@@ -227,15 +206,11 @@
         ExpressionFactory factory = new ExpressionFactory();
         Expression parentExpression = factory.equalTo(Key.AGENT_ID, "testKey");
         DescriptorMetadata metadata = new DescriptorMetadata();
-        StatementDescriptor desc = mock(StatementDescriptor.class);
-        Category category = mock(Category.class);
-        // want to have a null retval of vmId
-        when(category.getKey(eq(Key.VM_ID.getName()))).thenReturn(null);
-        when(desc.getCategory()).thenReturn(category);
         assertFalse(metadata.hasAgentId());
         assertFalse(metadata.hasVmId());
-        VmIdFilter<?> filter = new VmIdFilter<>(roles);
-        FilterResult result = filter.applyFilter(desc, metadata, parentExpression);
+        VmIdFilter<FooPojo> filter = new VmIdFilter<>(roles);
+        // want to have a null retval of vmId
+        FilterResult result = filter.applyFilter(TEST_DESC_NULL_VM_ID, metadata, parentExpression);
         assertEquals(ResultType.QUERY_EXPRESSION, result.getType());
         assertNotNull(result.getFilterExpression());
         assertEquals(parentExpression, result.getFilterExpression());
--- a/web/server/src/test/java/com/redhat/thermostat/web/server/auth/VmUsernameFilterTest.java	Tue Oct 22 18:20:04 2013 +0200
+++ b/web/server/src/test/java/com/redhat/thermostat/web/server/auth/VmUsernameFilterTest.java	Mon Oct 28 15:30:44 2013 +0100
@@ -40,8 +40,6 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
 import java.util.HashSet;
 import java.util.Set;
@@ -62,7 +60,7 @@
 import com.redhat.thermostat.web.server.auth.FilterResult.ResultType;
 
 public class VmUsernameFilterTest {
-
+    
     @Test
     public void testReadAll() {
         Set<BasicRole> roles = new HashSet<>();
@@ -97,9 +95,7 @@
         roles.add(vmUsernameRole);
         
         DescriptorMetadata metadata = new DescriptorMetadata();
-        @SuppressWarnings("unchecked")
-        StatementDescriptor<VmInfo> desc = mock(StatementDescriptor.class);
-        when(desc.getCategory()).thenReturn(VmInfoDAO.vmInfoCategory);
+        StatementDescriptor<VmInfo> desc = new StatementDescriptor<>(VmInfoDAO.vmInfoCategory, "QUERY " + VmInfoDAO.vmInfoCategory.getName());
         
         Set<String> usernames = new HashSet<>();
         usernames.add(testUsername);
@@ -121,9 +117,7 @@
         roles.add(vmUsernameRole);
         
         DescriptorMetadata metadata = new DescriptorMetadata();
-        @SuppressWarnings("unchecked")
-        StatementDescriptor<VmInfo> desc = mock(StatementDescriptor.class);
-        when(desc.getCategory()).thenReturn(VmInfoDAO.vmInfoCategory);
+        StatementDescriptor<VmInfo> desc = new StatementDescriptor<>(VmInfoDAO.vmInfoCategory, "QUERY " + VmInfoDAO.vmInfoCategory.getName());
         
         Set<String> usernames = new HashSet<>();
         usernames.add(testUsername);
@@ -145,9 +139,7 @@
         Set<BasicRole> roles = new HashSet<>();
         
         DescriptorMetadata metadata = new DescriptorMetadata();
-        @SuppressWarnings("unchecked")
-        StatementDescriptor<AgentInformation> desc = mock(StatementDescriptor.class);
-        when(desc.getCategory()).thenReturn(AgentInfoDAO.CATEGORY);
+        StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, "QUERY " + AgentInfoDAO.CATEGORY.getName());
         
         VmUsernameFilter<AgentInformation> filter = new VmUsernameFilter<>(roles);
         FilterResult result = filter.applyFilter(desc, metadata, null);
@@ -160,9 +152,7 @@
         Set<BasicRole> roles = new HashSet<>();
         
         DescriptorMetadata metadata = new DescriptorMetadata();
-        @SuppressWarnings("unchecked")
-        StatementDescriptor<AgentInformation> desc = mock(StatementDescriptor.class);
-        when(desc.getCategory()).thenReturn(AgentInfoDAO.CATEGORY);
+        StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, "QUERY " + AgentInfoDAO.CATEGORY.getName());
         
         ExpressionFactory factory = new ExpressionFactory();
         Expression parentExpression = factory.equalTo(Key.AGENT_ID, "testKey");