changeset 1282:a4c836ada099

Improve prepared parameter serialization. Reviewed-by: neugens Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-October/008374.html
author Severin Gehwolf <sgehwolf@redhat.com>
date Wed, 02 Oct 2013 15:07:46 +0200
parents 7a4e7c3c4a1d
children 2a4b306aefe6
files storage/core/src/main/java/com/redhat/thermostat/storage/core/PreparedParameter.java storage/core/src/main/java/com/redhat/thermostat/storage/core/PreparedParameters.java storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/BasicDescriptorParser.java storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/LimitExpression.java storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/SortMember.java storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/TerminalNode.java storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/UnfinishedValueNode.java storage/core/src/test/java/com/redhat/thermostat/storage/core/PreparedParamentersTest.java storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/BasicDescriptorParserTest.java storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/LimitExpressionTest.java storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/ParsedStatementImplTest.java storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/SortMemberTest.java storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/TerminalNodeTest.java web/client/src/test/java/com/redhat/thermostat/web/client/internal/WebStorageTest.java web/common/src/main/java/com/redhat/thermostat/web/common/PreparedParameterSerializer.java web/common/src/test/java/com/redhat/thermostat/web/common/PreparedParameterSerializerTest.java web/common/src/test/java/com/redhat/thermostat/web/common/PreparedParametersSerializerTest.java web/common/src/test/java/com/redhat/thermostat/web/common/WebPreparedStatementSerializerTest.java
diffstat 18 files changed, 478 insertions(+), 278 deletions(-) [+]
line wrap: on
line diff
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/core/PreparedParameter.java	Wed Oct 02 18:14:39 2013 +0200
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/core/PreparedParameter.java	Wed Oct 02 15:07:46 2013 +0200
@@ -44,16 +44,13 @@
 public class PreparedParameter {
 
     private Object value;
-    // The simple type of value. Even if value is an array.
+    // The type of the value field. This is the instantiable component type for
+    // Pojo array values.
     private Class<?> type;
-    // true if and only if value is an array of simple types
-    // as specified by the type instance variable.
-    private boolean isArrayType;
     
-    PreparedParameter(Object value, Class<?> type, boolean isArrayType) {
+    PreparedParameter(Object value, Class<?> type) {
         this.value = value;
         this.type = type;
-        this.isArrayType = isArrayType;
     }
     
     public PreparedParameter() {
@@ -75,12 +72,4 @@
     public void setType(Class<?> type) {
         this.type = type;
     }
-
-    public boolean isArrayType() {
-        return isArrayType;
-    }
-
-    public void setArrayType(boolean isArrayType) {
-        this.isArrayType = isArrayType;
-    }
 }
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/core/PreparedParameters.java	Wed Oct 02 18:14:39 2013 +0200
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/core/PreparedParameters.java	Wed Oct 02 15:07:46 2013 +0200
@@ -37,6 +37,7 @@
 package com.redhat.thermostat.storage.core;
 
 import java.lang.reflect.Modifier;
+import java.util.Objects;
 
 import com.redhat.thermostat.storage.model.Pojo;
 
@@ -60,66 +61,72 @@
     
     @Override
     public void setLong(int paramIndex, long paramValue) {
-        setType(paramIndex, paramValue, Long.class, false);
+        setType(paramIndex, paramValue, long.class);
     }
 
     @Override
     public void setLongList(int paramIndex, long[] paramValue) {
-        setType(paramIndex, paramValue, Long.class, true);
+        setType(paramIndex, paramValue, long[].class);
     }
 
     @Override
     public void setInt(int paramIndex, int paramValue) {
-        setType(paramIndex, paramValue, Integer.class, false);
+        setType(paramIndex, paramValue, int.class);
     }
 
     @Override
     public void setIntList(int paramIndex, int[] paramValue) {
-        setType(paramIndex, paramValue, Integer.class, true);
+        setType(paramIndex, paramValue, int[].class);
     }
 
     @Override
     public void setBoolean(int paramIndex, boolean paramValue) {
-        setType(paramIndex, paramValue, Boolean.class, false);
+        setType(paramIndex, paramValue, boolean.class);
     }
 
     @Override
     public void setBooleanList(int paramIndex, boolean[] paramValue) {
-        setType(paramIndex, paramValue, Boolean.class, true);
+        setType(paramIndex, paramValue, boolean[].class);
     }
 
     @Override
     public void setString(int paramIndex, String paramValue) {
-        setType(paramIndex, paramValue, String.class, false);
+        setType(paramIndex, paramValue, String.class);
     }
 
     @Override
     public void setStringList(int paramIndex, String[] paramValue) {
-        setType(paramIndex, paramValue, String.class, true);
+        setType(paramIndex, paramValue, String[].class);
     }
 
     @Override
     public void setDouble(int paramIndex, double paramValue) {
-        setType(paramIndex, paramValue, Double.class, false);
+        setType(paramIndex, paramValue, double.class);
     }
 
     @Override
     public void setDoubleList(int paramIndex, double[] paramValue) {
-        setType(paramIndex, paramValue, Double.class, true);
+        setType(paramIndex, paramValue, double[].class);
     }
 
     @Override
     public void setPojo(int paramIndex, Pojo paramValue) {
+        // null Pojo value would make array and non-array types
+        // indistinguishable for serialization
+        Objects.requireNonNull(paramValue);
         Class<?> runtimeType = paramValue.getClass();
         performPojoChecks(runtimeType, "Type");
-        setType(paramIndex, paramValue, runtimeType, false);
+        setType(paramIndex, paramValue, runtimeType);
     }
 
     @Override
     public void setPojoList(int paramIndex, Pojo[] paramValue) {
+        // null Pojo value would make array and non-array types
+        // indistinguishable for serialization
+        Objects.requireNonNull(paramValue);
         Class<?> componentType = paramValue.getClass().getComponentType();
         performPojoChecks(componentType, "Component type");
-        setType(paramIndex, paramValue, componentType, true);
+        setType(paramIndex, paramValue, componentType);
     }
     
     private void performPojoChecks(Class<?> type, String errorMsgPrefix) {
@@ -132,11 +139,11 @@
         }
     }
 
-    private void setType(int paramIndex, Object paramValue, Class<?> paramType, boolean isArrayType) {
+    private void setType(int paramIndex, Object paramValue, Class<?> paramType) {
         if (paramIndex >= params.length) {
             throw new IllegalArgumentException("Parameter index '" + paramIndex + "' out of range.");
         }
-        PreparedParameter param = new PreparedParameter(paramValue, paramType, isArrayType);
+        PreparedParameter param = new PreparedParameter(paramValue, paramType);
         params[paramIndex] = param;
     }
     
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/BasicDescriptorParser.java	Wed Oct 02 18:14:39 2013 +0200
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/BasicDescriptorParser.java	Wed Oct 02 15:07:46 2013 +0200
@@ -544,12 +544,11 @@
             patchNode.setParameterIndex(placeHolderCount - 1);
             patchNode.setLHS(isLHS);
             // figure out the expected type
-            FreeVarTypeToken expectedType = getType(term.substring(1));
+            Class<?> expectedType = getType(term.substring(1));
             if (expectedType == null) {
                 throw new DescriptorParsingException("Unknown type of free parameter: '" + term + "'");
             }
-            patchNode.setType(expectedType.componentType);
-            patchNode.setArrayType(expectedType.isArrayType);
+            patchNode.setType(expectedType);
             node.setValue(patchNode);
             return;
         }
@@ -609,7 +608,7 @@
         return term.substring(1, term.length() - 1);
     }
 
-    private FreeVarTypeToken getType(String term) {
+    private Class<?> getType(String term) {
         if (term.equals("")) {
             // illegal type
             return null;
@@ -617,53 +616,53 @@
         // free variable types can have 1 or 2 characters.
         assert(term.length() > 0 && term.length() < 3);
         char switchChar = term.charAt(0);
-        FreeVarTypeToken typeToken = null;
+        Class<?> typeToken = null;
         switch (switchChar) {
         case 'i': {
             if (term.length() == 1) {
-                typeToken = new FreeVarTypeToken(Integer.class, false);
+                typeToken = int.class;
             } else if (term.length() == 2 && term.charAt(1) == '[') {
-                typeToken = new FreeVarTypeToken(Integer.class, true);
+                typeToken = int[].class;
             }
             break;
         }
         case 'l': {
             if (term.length() == 1) {
-                typeToken = new FreeVarTypeToken(Long.class, false);
+                typeToken = long.class;
             } else if (term.length() == 2 && term.charAt(1) == '[') {
-                typeToken = new FreeVarTypeToken(Long.class, true);
+                typeToken = long[].class;
             }
             break;
         }
         case 's': {
             if (term.length() == 1) {
-                typeToken = new FreeVarTypeToken(String.class, false);
+                typeToken = String.class;
             } else if (term.length() == 2 && term.charAt(1) == '[') {
-                typeToken = new FreeVarTypeToken(String.class, true);
+                typeToken = String[].class;
             }
             break;
         }
         case 'b': {
             if (term.length() == 1) {
-                typeToken = new FreeVarTypeToken(Boolean.class, false);
+                typeToken = boolean.class;
             } else if (term.length() == 2 && term.charAt(1) == '[') {
-                typeToken = new FreeVarTypeToken(Boolean.class, true);
+                typeToken = boolean[].class;
             }
             break;
         }
         case 'd': {
             if (term.length() == 1) {
-                typeToken = new FreeVarTypeToken(Double.class, false);
+                typeToken = double.class;
             } else if (term.length() == 2 && term.charAt(1) == '[') {
-                typeToken = new FreeVarTypeToken(Double.class, true);
+                typeToken = double[].class;
             }
             break;
         }
         case 'p': {
             if (term.length() == 1) {
-                typeToken = new FreeVarTypeToken(Pojo.class, false);
+                typeToken = Pojo.class;
             } else if (term.length() == 2 && term.charAt(1) == '[') {
-                typeToken = new FreeVarTypeToken(Pojo.class, true);
+                typeToken = Pojo[].class;
             }
             break;
         }
@@ -822,15 +821,4 @@
             throw new DescriptorParsingException("Unknown statement type: '" + tokens[currTokenIndex] + "'");
         }
     }
-    
-    private static class FreeVarTypeToken {
-        
-        private final boolean isArrayType;
-        private final Class<?> componentType;
-        
-        private FreeVarTypeToken(Class<?> componentType, boolean isArrayType) {
-            this.isArrayType = isArrayType;
-            this.componentType = componentType;
-        }
-    }
 }
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/LimitExpression.java	Wed Oct 02 18:14:39 2013 +0200
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/LimitExpression.java	Wed Oct 02 15:07:46 2013 +0200
@@ -68,7 +68,7 @@
             try {
                 PreparedParameter param = params[unfinished.getParameterIndex()];
                 Class<?> typeClass = param.getType();
-                if (typeClass != Integer.class || param.isArrayType()) {
+                if (typeClass != int.class) {
                     String msg = "Invalid parameter type for limit expression. Expected integer!";
                     IllegalArgumentException e = new IllegalArgumentException(msg);
                     throw e;
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/SortMember.java	Wed Oct 02 18:14:39 2013 +0200
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/SortMember.java	Wed Oct 02 15:07:46 2013 +0200
@@ -82,7 +82,7 @@
                 Unfinished unfinished = (Unfinished)getSortKey();
                 PreparedParameter p = params[unfinished.getParameterIndex()];
                 // Should only allow patching of ?s type NOT ?s[
-                if (p.getType() != String.class || p.isArrayType()) {
+                if (p.getType() != String.class) {
                     String msg = "Illegal parameter type for index "
                             + unfinished.getParameterIndex()
                             + ". Expected String!";
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/TerminalNode.java	Wed Oct 02 18:14:39 2013 +0200
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/TerminalNode.java	Wed Oct 02 15:07:46 2013 +0200
@@ -92,16 +92,24 @@
             PreparedParameter param) throws IllegalPatchException {
         if (patch.getType() == Pojo.class) {
             // handle pojo case
+            Object value = param.getValue();
             if (Pojo.class.isAssignableFrom(param.getType()) &&
-                    patch.isArrayType() == param.isArrayType()) {
+                    value != null && !value.getClass().isArray()) {
                 return; // pojo-type match: OK
             }
             // dead-end
             IllegalArgumentException iae = constructIllegalArgumentException(patch, param);
             throw new IllegalPatchException(iae);
+        } else if (patch.getType() == Pojo[].class) {
+            // handle pojo list case
+            Object value = param.getValue();
+            if (Pojo.class.isAssignableFrom(param.getType()) && value != null
+                    && value.getClass().isArray()) {
+                return; // pojo-list type match: OK
+            }
         } else {
             // primitive types or primitive list types
-            if (param.getType() != patch.getType() || param.isArrayType() != patch.isArrayType()) {
+            if (param.getType() != patch.getType()) {
                 IllegalArgumentException iae = constructIllegalArgumentException(patch, param);
                 throw new IllegalPatchException(iae);
             }
@@ -111,11 +119,14 @@
         
     private IllegalArgumentException constructIllegalArgumentException(
             UnfinishedValueNode patch, PreparedParameter param) {
-        String patchArrayPrefix = patch.isArrayType() ? "[" : "";
-        String paramArrayPrefix = param.isArrayType() ? "[" : "";
+        Object value = param.getValue();
+        String paramArrayPrefix = "";
+        if (value != null && value.getClass().isArray()) {
+            paramArrayPrefix = "[";
+        }
         String msg = TerminalNode.class.getSimpleName()
                 + " invalid type when attempting to patch. Expected "
-                + patchArrayPrefix + patch.getType().getName() + " but was "
+                + patch.getType().getName() + " but was "
                 + paramArrayPrefix + param.getType().getName();
         IllegalArgumentException iae = new IllegalArgumentException(msg);
         return iae;
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/UnfinishedValueNode.java	Wed Oct 02 18:14:39 2013 +0200
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/UnfinishedValueNode.java	Wed Oct 02 15:07:46 2013 +0200
@@ -54,9 +54,6 @@
     private boolean isLHS;
     // Specifies the expected (component) type of this free parameter.
     private Class<?> type;
-    // Specifies if the free parameter is an array type. If so, the
-    // type instance variable represents the component type.
-    private boolean isArrayType;
 
     Class<?> getType() {
         return type;
@@ -83,14 +80,6 @@
     public void setParameterIndex(int parameterIndex) {
         this.parameterIndex = parameterIndex;
     }
-    
-    public boolean isArrayType() {
-        return isArrayType;
-    }
-
-    public void setArrayType(boolean isArrayType) {
-        this.isArrayType = isArrayType;
-    }
 
     @Override
     public String toString() {
@@ -109,13 +98,12 @@
         }
         UnfinishedValueNode o = (UnfinishedValueNode)other;
         return basics && Objects.equals(isLHS(), o.isLHS) &&
-                Objects.equals(getType(), o.getType()) &&
-                        isArrayType() == o.isArrayType();
+                Objects.equals(getType(), o.getType());
     }
     
     @Override
     public int hashCode() {
-        return Objects.hash(getParameterIndex(), isLHS(), getType(), isArrayType());
+        return Objects.hash(getParameterIndex(), isLHS(), getType());
     }
 
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/core/src/test/java/com/redhat/thermostat/storage/core/PreparedParamentersTest.java	Wed Oct 02 15:07:46 2013 +0200
@@ -0,0 +1,137 @@
+/*
+ * 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.storage.core;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.storage.model.AgentInformation;
+
+public class PreparedParamentersTest {
+
+    @Test
+    public void cannotSetNullPojo() {
+        PreparedParameters params = new PreparedParameters(1);
+        try {
+            params.setPojo(0, null);
+            fail("should not be able to set null Pojo");
+        } catch (NullPointerException e) {
+            // passs
+        }
+    }
+    
+    @Test
+    public void cannotSetNullPojoList() {
+        PreparedParameters params = new PreparedParameters(1);
+        try {
+            params.setPojoList(0, null);
+            fail("should not be able to set null Pojo list");
+        } catch (NullPointerException e) {
+            // passs
+        }
+    }
+    
+    @Test
+    public void canSetPojoType() {
+        PreparedParameters params = new PreparedParameters(1);
+        AgentInformation info = new AgentInformation(); 
+        params.setPojo(0, info);
+        assertEquals(AgentInformation.class, params.getParams()[0].getType());
+        assertEquals(info, params.getParams()[0].getValue());
+    }
+    
+    @Test
+    public void canSetPojoListType() {
+        PreparedParameters params = new PreparedParameters(1);
+        AgentInformation info = new AgentInformation();
+        params.setPojoList(0, new AgentInformation[] { info });
+        PreparedParameter p = params.getParams()[0];
+        assertEquals("Type should be component type", AgentInformation.class, p.getType());
+        assertEquals(info, ((AgentInformation[])p.getValue())[0]);
+    }
+    
+    @Test
+    public void testTypeInt() {
+        PreparedParameters params = new PreparedParameters(1);
+        params.setInt(0, -1);
+        assertEquals(int.class, params.getParams()[0].getType());
+    }
+    
+    @Test
+    public void testTypeIntList() {
+        PreparedParameters params = new PreparedParameters(1);
+        params.setIntList(0, new int[] { -1 });
+        assertEquals(int[].class, params.getParams()[0].getType());
+    }
+    
+    @Test
+    public void testTypeLong() {
+        PreparedParameters params = new PreparedParameters(1);
+        params.setLong(0, -1);
+        assertEquals(long.class, params.getParams()[0].getType());
+    }
+    
+    @Test
+    public void testTypeLongList() {
+        PreparedParameters params = new PreparedParameters(1);
+        params.setLongList(0, new long[] { -1 });
+        assertEquals(long[].class, params.getParams()[0].getType());
+    }
+    
+    @Test
+    public void testTypeBoolList() {
+        PreparedParameters params = new PreparedParameters(1);
+        params.setBooleanList(0, new boolean[] { true } );
+        assertEquals(boolean[].class, params.getParams()[0].getType());
+    }
+    
+    @Test
+    public void testTypeDouble() {
+        PreparedParameters params = new PreparedParameters(1);
+        params.setDouble(0, Math.E);
+        assertEquals(double.class, params.getParams()[0].getType());
+    }
+    
+    @Test
+    public void testTypeDoubleList() {
+        PreparedParameters params = new PreparedParameters(1);
+        params.setDoubleList(0, new double[] { Math.E });
+        assertEquals(double[].class, params.getParams()[0].getType());
+    }
+}
--- a/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/BasicDescriptorParserTest.java	Wed Oct 02 18:14:39 2013 +0200
+++ b/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/BasicDescriptorParserTest.java	Wed Oct 02 15:07:46 2013 +0200
@@ -221,7 +221,7 @@
         String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != ?d";
         UnfinishedValueNode unfinished = new UnfinishedValueNode();
         unfinished.setLHS(false);
-        unfinished.setType(Double.class);
+        unfinished.setType(double.class);
         unfinished.setParameterIndex(0);
         doTestType(descrString, unfinished, 1);
     }
@@ -236,8 +236,7 @@
         String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = ?s[";
         UnfinishedValueNode unfinished = new UnfinishedValueNode();
         unfinished.setLHS(false);
-        unfinished.setType(String.class);
-        unfinished.setArrayType(true);
+        unfinished.setType(String[].class);
         unfinished.setParameterIndex(0);
         doListTypeTest(descString, unfinished);
     }
@@ -252,8 +251,7 @@
         String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = ?d[";
         UnfinishedValueNode unfinished = new UnfinishedValueNode();
         unfinished.setLHS(false);
-        unfinished.setType(Double.class);
-        unfinished.setArrayType(true);
+        unfinished.setType(double[].class);
         unfinished.setParameterIndex(0);
         doListTypeTest(descString, unfinished);
     }
@@ -268,8 +266,7 @@
         String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = ?i[";
         UnfinishedValueNode unfinished = new UnfinishedValueNode();
         unfinished.setLHS(false);
-        unfinished.setType(Integer.class);
-        unfinished.setArrayType(true);
+        unfinished.setType(int[].class);
         unfinished.setParameterIndex(0);
         doListTypeTest(descString, unfinished);
     }
@@ -284,8 +281,7 @@
         String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = ?b[";
         UnfinishedValueNode unfinished = new UnfinishedValueNode();
         unfinished.setLHS(false);
-        unfinished.setType(Boolean.class);
-        unfinished.setArrayType(true);
+        unfinished.setType(boolean[].class);
         unfinished.setParameterIndex(0);
         doListTypeTest(descString, unfinished);
     }
@@ -300,8 +296,7 @@
         String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = ?l[";
         UnfinishedValueNode unfinished = new UnfinishedValueNode();
         unfinished.setLHS(false);
-        unfinished.setType(Long.class);
-        unfinished.setArrayType(true);
+        unfinished.setType(long[].class);
         unfinished.setParameterIndex(0);
         doListTypeTest(descString, unfinished);
     }
@@ -331,8 +326,7 @@
         String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = ?p[";
         UnfinishedValueNode unfinished = new UnfinishedValueNode();
         unfinished.setLHS(false);
-        unfinished.setType(Pojo.class);
-        unfinished.setArrayType(true);
+        unfinished.setType(Pojo[].class);
         unfinished.setParameterIndex(0);
         doListTypeTest(descString, unfinished);
     }
@@ -371,7 +365,7 @@
         String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != ?i";
         UnfinishedValueNode unfinished = new UnfinishedValueNode();
         unfinished.setLHS(false);
-        unfinished.setType(Integer.class);
+        unfinished.setType(int.class);
         unfinished.setParameterIndex(0);
         doTestType(descrString, unfinished, 1);
     }
@@ -381,7 +375,7 @@
         String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != ?l";
         UnfinishedValueNode unfinished = new UnfinishedValueNode();
         unfinished.setLHS(false);
-        unfinished.setType(Long.class);
+        unfinished.setType(long.class);
         unfinished.setParameterIndex(0);
         doTestType(descrString, unfinished, 1);
     }
@@ -391,7 +385,7 @@
         String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != ?b";
         UnfinishedValueNode unfinished = new UnfinishedValueNode();
         unfinished.setLHS(false);
-        unfinished.setType(Boolean.class);
+        unfinished.setType(boolean.class);
         unfinished.setParameterIndex(0);
         doTestType(descrString, unfinished, 1);
     }
@@ -540,7 +534,7 @@
         lessThan.setLeftChild(e);
         UnfinishedValueNode f = new UnfinishedValueNode();
         f.setParameterIndex(0);
-        f.setType(Integer.class);
+        f.setType(int.class);
         f.setLHS(false);
         TerminalNode fReal = new TerminalNode(lessThan);
         fReal.setValue(f);
@@ -668,7 +662,7 @@
         lessThan.setLeftChild(e);
         UnfinishedValueNode f = new UnfinishedValueNode();
         f.setParameterIndex(0);
-        f.setType(Integer.class);
+        f.setType(int.class);
         f.setLHS(false);
         TerminalNode fReal = new TerminalNode(lessThan);
         fReal.setValue(f);
@@ -1029,7 +1023,7 @@
         UnfinishedValueNode patch2 = new UnfinishedValueNode();
         patch2.setParameterIndex(1);
         patch2.setLHS(false);
-        patch2.setType(Integer.class);
+        patch2.setType(int.class);
         TerminalNode y = new TerminalNode(greaterEqual);
         y.setValue(patch2);
         greaterEqual.setRightChild(y);
@@ -1227,7 +1221,7 @@
         UnfinishedValueNode patch2 = new UnfinishedValueNode();
         patch2.setLHS(false);
         patch2.setParameterIndex(2);
-        patch2.setType(Boolean.class);
+        patch2.setType(boolean.class);
         TerminalNode y = new TerminalNode(lessThan);
         y.setValue(patch2);
         lessThan.setRightChild(y);
@@ -1530,7 +1524,7 @@
         UnfinishedValueNode node = new UnfinishedValueNode();
         node.setParameterIndex(1);
         node.setLHS(false);
-        node.setType(Integer.class);
+        node.setType(int.class);
         fooVal.setValue(node);
         equality.setRightChild(fooVal);
         assertTrue(WhereExpressions.equals(expectedWhere, where));
--- a/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/LimitExpressionTest.java	Wed Oct 02 18:14:39 2013 +0200
+++ b/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/LimitExpressionTest.java	Wed Oct 02 15:07:46 2013 +0200
@@ -62,8 +62,7 @@
     @Test
     public void canPatchWithInt() {
         PreparedParameter p = new PreparedParameter();
-        p.setType(Integer.class);
-        p.setArrayType(false);
+        p.setType(int.class);
         p.setValue(3);
         
         PatchedLimitExpression pLimit = null;
@@ -80,8 +79,7 @@
     @Test
     public void rejectPatchWithIntList() {
         PreparedParameter p = new PreparedParameter();
-        p.setType(Integer.class);
-        p.setArrayType(true);
+        p.setType(int[].class);
         p.setValue(new int[] { 3 });
         
         try {
@@ -97,7 +95,6 @@
     public void rejectPatchWithString() {
         PreparedParameter p = new PreparedParameter();
         p.setType(String.class);
-        p.setArrayType(false);
         p.setValue("foo");
         
         try {
--- a/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/ParsedStatementImplTest.java	Wed Oct 02 18:14:39 2013 +0200
+++ b/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/ParsedStatementImplTest.java	Wed Oct 02 15:07:46 2013 +0200
@@ -119,7 +119,7 @@
         TerminalNode d = new TerminalNode(rightEqual);
         UnfinishedValueNode dPatch = new UnfinishedValueNode();
         dPatch.setParameterIndex(1);
-        dPatch.setType(Integer.class);
+        dPatch.setType(int.class);
         d.setValue(dPatch);
         rightEqual.setRightChild(d);
         suffixExpn.setWhereExpn(expn);
@@ -176,7 +176,7 @@
         TerminalNode b = new TerminalNode(and);
         UnfinishedValueNode bPatch = new UnfinishedValueNode();
         bPatch.setParameterIndex(0);
-        bPatch.setType(Boolean.class);
+        bPatch.setType(boolean.class);
         b.setValue(bPatch);
         and.setLeftChild(a);
         and.setRightChild(b);
@@ -244,7 +244,7 @@
         TerminalNode b = new TerminalNode(and);
         UnfinishedValueNode bPatch = new UnfinishedValueNode();
         bPatch.setParameterIndex(1);
-        bPatch.setType(Boolean.class);
+        bPatch.setType(boolean.class);
         b.setValue(bPatch);
         and.setLeftChild(a);
         and.setRightChild(b);
@@ -341,7 +341,7 @@
         UnfinishedValueNode timeStampUnfinished = new UnfinishedValueNode();
         timeStampUnfinished.setLHS(false);
         timeStampUnfinished.setParameterIndex(1);
-        timeStampUnfinished.setType(Long.class);
+        timeStampUnfinished.setType(long.class);
         timeStampVal.setValue(timeStampUnfinished);
         fooTimeStamp.setValue(timeStampVal);
         setList.addValue(fooTimeStamp);
@@ -396,8 +396,7 @@
         TerminalNode somePropertyVal = new TerminalNode(null);
         UnfinishedValueNode unfinishedPojoList = new UnfinishedValueNode();
         unfinishedPojoList.setLHS(false);
-        unfinishedPojoList.setType(Pojo.class);
-        unfinishedPojoList.setArrayType(true);
+        unfinishedPojoList.setType(Pojo[].class);
         unfinishedPojoList.setParameterIndex(0);
         somePropertyVal.setValue(unfinishedPojoList);
         SetListValue value = new SetListValue();
@@ -476,7 +475,7 @@
         UnfinishedValueNode timeStampUnfinished = new UnfinishedValueNode();
         timeStampUnfinished.setLHS(false);
         timeStampUnfinished.setParameterIndex(1);
-        timeStampUnfinished.setType(Long.class);
+        timeStampUnfinished.setType(long.class);
         timeStampVal.setValue(timeStampUnfinished);
         fooTimeStamp.setValue(timeStampVal);
         setList.addValue(fooTimeStamp);
@@ -491,7 +490,7 @@
         TerminalNode fooPatch = new TerminalNode(equals);
         UnfinishedValueNode patch2 = new UnfinishedValueNode();
         patch2.setLHS(false);
-        patch2.setType(Integer.class);
+        patch2.setType(int.class);
         patch2.setParameterIndex(2);
         fooPatch.setValue(patch2);
         TerminalNode foo = new TerminalNode(equals);
@@ -558,7 +557,7 @@
         TerminalNode fooPatch = new TerminalNode(equals);
         UnfinishedValueNode patch2 = new UnfinishedValueNode();
         patch2.setLHS(false);
-        patch2.setType(Integer.class);
+        patch2.setType(int.class);
         patch2.setParameterIndex(1);
         fooPatch.setValue(patch2);
         TerminalNode foo = new TerminalNode(equals);
--- a/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/SortMemberTest.java	Wed Oct 02 18:14:39 2013 +0200
+++ b/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/SortMemberTest.java	Wed Oct 02 15:07:46 2013 +0200
@@ -66,7 +66,6 @@
     public void testPatchWithString() {
         PreparedParameter p = new PreparedParameter();
         p.setType(String.class);
-        p.setArrayType(false);
         p.setValue("foo");
         PatchedSortMemberExpression expn = null;
         try {
@@ -83,8 +82,7 @@
     @Test
     public void rejectPatchWithStringList() {
         PreparedParameter p = new PreparedParameter();
-        p.setType(String.class);
-        p.setArrayType(true);
+        p.setType(String[].class);
         p.setValue(new String[] { "foo" });
         try {
             member.patch(new PreparedParameter[] { p });
@@ -98,8 +96,7 @@
     @Test
     public void rejectPatchWithNumber() {
         PreparedParameter p = new PreparedParameter();
-        p.setType(Integer.class);
-        p.setArrayType(false);
+        p.setType(int.class);
         p.setValue(1);
         try {
             member.patch(new PreparedParameter[] { p });
--- a/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/TerminalNodeTest.java	Wed Oct 02 18:14:39 2013 +0200
+++ b/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/TerminalNodeTest.java	Wed Oct 02 15:07:46 2013 +0200
@@ -58,13 +58,11 @@
         unfinished.setLHS(false);
         unfinished.setParameterIndex(0);
         unfinished.setType(String.class);
-        unfinished.setArrayType(false);
         
         node.setValue(unfinished);
         
         PreparedParameter p = new PreparedParameter();
         p.setType(String.class);
-        p.setArrayType(false);
         p.setValue("foo-bar");
         
         PatchedWhereExpression expn = null;
@@ -86,13 +84,11 @@
         unfinished.setLHS(false);
         unfinished.setParameterIndex(0);
         unfinished.setType(Pojo.class);
-        unfinished.setArrayType(false);
         
         node.setValue(unfinished);
         
         PreparedParameter p = new PreparedParameter();
         p.setType(AgentInformation.class);
-        p.setArrayType(false);
         AgentInformation info = new AgentInformation("foo-bar");
         p.setValue(info);
         
@@ -115,13 +111,11 @@
         unfinished.setLHS(false);
         unfinished.setParameterIndex(0);
         unfinished.setType(String.class);
-        unfinished.setArrayType(false);
         
         node.setValue(unfinished);
         
         PreparedParameter p = new PreparedParameter();
-        p.setType(String.class);
-        p.setArrayType(true);
+        p.setType(String[].class);
         p.setValue(new String[] { "foo-bar" });
         
         try {
@@ -140,13 +134,11 @@
         unfinished.setLHS(false);
         unfinished.setParameterIndex(0);
         unfinished.setType(Pojo.class);
-        unfinished.setArrayType(false);
         
         node.setValue(unfinished);
         
         PreparedParameter p = new PreparedParameter();
         p.setType(AgentInformation.class);
-        p.setArrayType(true);
         AgentInformation info = new AgentInformation("testing");
         p.setValue(new AgentInformation[] { info });
         
@@ -165,14 +157,12 @@
         UnfinishedValueNode unfinished = new UnfinishedValueNode();
         unfinished.setLHS(false);
         unfinished.setParameterIndex(0);
-        unfinished.setType(Integer.class);
-        unfinished.setArrayType(false);
+        unfinished.setType(int.class);
         
         node.setValue(unfinished);
         
         PreparedParameter p = new PreparedParameter();
-        p.setType(String.class);
-        p.setArrayType(true);
+        p.setType(String[].class);
         p.setValue(new String[] { "foo-bar" });
         
         try {
--- a/web/client/src/test/java/com/redhat/thermostat/web/client/internal/WebStorageTest.java	Wed Oct 02 18:14:39 2013 +0200
+++ b/web/client/src/test/java/com/redhat/thermostat/web/client/internal/WebStorageTest.java	Wed Oct 02 15:07:46 2013 +0200
@@ -414,7 +414,6 @@
         stmt.setString(0, "fluff");
         assertEquals("fluff", params.getParams()[0].getValue());
         assertEquals(String.class, params.getParams()[0].getType());
-        assertFalse(params.getParams()[0].isArrayType());
         
         prepareServer(gson.toJson(PreparedStatementResponseCode.WRITE_GENERIC_FAILURE));
         
--- a/web/common/src/main/java/com/redhat/thermostat/web/common/PreparedParameterSerializer.java	Wed Oct 02 18:14:39 2013 +0200
+++ b/web/common/src/main/java/com/redhat/thermostat/web/common/PreparedParameterSerializer.java	Wed Oct 02 15:07:46 2013 +0200
@@ -65,47 +65,56 @@
 
     private static final Logger logger = LoggingUtils.getLogger(PreparedParameterSerializer.class);
     private static final String PROP_TYPE = "type";
-    private static final String PROP_IS_ARRAY_TYPE = "isArray";
     private static final String PROP_VALUE = "value";
-    private static final Set<Class<?>> WRAPPER_CLASSES;
-    // maps wrapper classes to primitives:
-    //    Integer.class => int.class , Double.class => double.class, etc.
-    private static final Map<Class<?>, Class<?>> TO_PRIMITIVE_ARRAY_MAP;
+    private static final Set<Class<?>> VALID_CLASSES;
+    private static final Set<Class<?>> PRIMITIVES_NOT_ALLOWING_NULL_VAL;
+    // maps type names to classes:
+    //    "int" => int.class , "double" => double.class, "[I" => int[].class
+    //    and so on.
+    private static final Map<String, Class<?>> CLASSES_LOOKUP_TABLE;
     
     static {
-        WRAPPER_CLASSES = new HashSet<>();
-        TO_PRIMITIVE_ARRAY_MAP = new HashMap<>();
-        WRAPPER_CLASSES.add(Integer.class);
-        TO_PRIMITIVE_ARRAY_MAP.put(Integer.class, int.class);
-        WRAPPER_CLASSES.add(Long.class);
-        TO_PRIMITIVE_ARRAY_MAP.put(Long.class, long.class);
-        WRAPPER_CLASSES.add(Boolean.class);
-        TO_PRIMITIVE_ARRAY_MAP.put(Boolean.class, boolean.class);
-        WRAPPER_CLASSES.add(Double.class);
-        TO_PRIMITIVE_ARRAY_MAP.put(Double.class, double.class);
-        
+        VALID_CLASSES = new HashSet<>();
+        CLASSES_LOOKUP_TABLE = new HashMap<>();
+        CLASSES_LOOKUP_TABLE.put(int.class.getName(), int.class);
+        CLASSES_LOOKUP_TABLE.put(long.class.getName(), long.class);
+        CLASSES_LOOKUP_TABLE.put(boolean.class.getName(), boolean.class);
+        CLASSES_LOOKUP_TABLE.put(double.class.getName(), double.class);
+        CLASSES_LOOKUP_TABLE.put(String.class.getName(), String.class);
+        CLASSES_LOOKUP_TABLE.put(int[].class.getName(), int[].class);
+        CLASSES_LOOKUP_TABLE.put(long[].class.getName(), long[].class);
+        CLASSES_LOOKUP_TABLE.put(boolean[].class.getName(), boolean[].class);
+        CLASSES_LOOKUP_TABLE.put(double[].class.getName(), double[].class);
+        CLASSES_LOOKUP_TABLE.put(String[].class.getName(), String[].class);
+        VALID_CLASSES.addAll(CLASSES_LOOKUP_TABLE.values());
+        PRIMITIVES_NOT_ALLOWING_NULL_VAL = new HashSet<>();
+        PRIMITIVES_NOT_ALLOWING_NULL_VAL.add(int.class);
+        PRIMITIVES_NOT_ALLOWING_NULL_VAL.add(long.class);
+        PRIMITIVES_NOT_ALLOWING_NULL_VAL.add(boolean.class);
+        PRIMITIVES_NOT_ALLOWING_NULL_VAL.add(double.class);
     }
     
     @Override
     public JsonElement serialize(PreparedParameter param, Type type,
             JsonSerializationContext ctxt) {
         JsonObject result = new JsonObject();
-        JsonElement valueElem = serializeValue(ctxt, param.getValue(), param.getType(), param.isArrayType());
+        JsonElement valueElem = serializeValue(ctxt, param.getValue(), param.getType());
         result.add(PROP_VALUE, valueElem);
         JsonPrimitive typeElem = new JsonPrimitive(param.getType().getName());
         result.add(PROP_TYPE, typeElem);
-        JsonPrimitive arrayType = new JsonPrimitive(param.isArrayType());
-        result.add(PROP_IS_ARRAY_TYPE, arrayType);
         return result;
     }
 
-    private JsonElement serializeValue(JsonSerializationContext ctxt, Object value, Class<?> compType, boolean isArray) {
+    private JsonElement serializeValue(JsonSerializationContext ctxt, Object value, Class<?> type) {
         JsonElement element;
-        if (isArray) {
-                Class<?> arrayType = Array.newInstance(compType, 0).getClass();
-                element = ctxt.serialize(value, arrayType);
+        // Special case pojo list types: the value class is of array type for
+        // them, but the type is the component type.
+        if (value != null && value.getClass().isArray() && !type.isArray()) {
+            assert(Pojo.class.isAssignableFrom(type));
+            Class<?> arrayType = Array.newInstance(type, 0).getClass();
+            element = ctxt.serialize(value, arrayType);
         } else {
-            element = ctxt.serialize(value, compType);
+            element = ctxt.serialize(value, type);
         }
         return element;
     }
@@ -120,53 +129,60 @@
         Class<?> typeVal = deserializeTypeVal(className);
         validateSaneClassName(typeVal);
         JsonElement valueElement = jsonElem.getAsJsonObject().get(PROP_VALUE);
-        JsonElement isArrayElement = jsonElem.getAsJsonObject().get(PROP_IS_ARRAY_TYPE);
-        boolean isArray = isArrayElement.getAsBoolean();
-        Object value = deserializeValue(ctxt, valueElement, typeVal, isArray);
+        Object value = deserializeValue(ctxt, valueElement, typeVal);
         PreparedParameter param = new PreparedParameter();
         param.setType(typeVal);
         param.setValue(value);
-        param.setArrayType(isArray);
         return param;
     }
 
     private Class<?> deserializeTypeVal(String className) {
         Class<?> typeVal = null;
-        try {
-            typeVal = Class.forName(className);
-        } catch (ClassNotFoundException e) {
-            logger.log(Level.WARNING, "Failed to resolve class type for '"
-                    + className + "'.");
+        if (CLASSES_LOOKUP_TABLE.containsKey(className)) {
+            typeVal = CLASSES_LOOKUP_TABLE.get(className);
+        } else {
+            try {
+                // We need this for Pojo + Pojo list type params. For pojo
+                // lists the name we get passed is the component type of the
+                // array.
+                typeVal = Class.forName(className);
+            } catch (ClassNotFoundException e) {
+                logger.log(Level.WARNING, "Failed to resolve class type for '"
+                        + className + "'.");
+            }
         }
-        ;
         return typeVal;
     }
 
     private Object deserializeValue(JsonDeserializationContext ctxt,
-            JsonElement valueElement, Class<?> valType, boolean isArray) {
-        if (!isArray) {
-            // By telling GSON the type, we get the correctly casted
-            // value back.
-            return ctxt.deserialize(valueElement, valType);
-        } else {
+            JsonElement valueElement, Class<?> valType) {
+        // special case for Pojo/Pojo list types. In that case, the valType
+        // is the component type for arrays. In order to distinguish pojo
+        // lists from pojos, we use JSON's array info about the value element.
+        if (valueElement != null && valueElement.isJsonArray() && !valType.isArray()) {
+            assert(Pojo.class.isAssignableFrom(valType));
             Class<?> arrayType = Array.newInstance(valType, 0).getClass();
-            Object array;
-            // Make sure we get primitive type arrays if this is an array type
-            // of one of the wrapped primitives.
-            if (WRAPPER_CLASSES.contains(valType)) {
-                Class<?> primType = Array.newInstance(TO_PRIMITIVE_ARRAY_MAP.get(valType), 0).getClass();
-                array = ctxt.deserialize(valueElement, primType);
-            } else {
-                array = ctxt.deserialize(valueElement, arrayType);
-            }
-            return array;
+            return ctxt.deserialize(valueElement, arrayType);
+        } else {
+            Object value = ctxt.deserialize(valueElement, valType);
+            validatePrimitivesForNull(value, valType);
+            return value;
         }
     }
 
-    // Allow wrapper classes, String + Pojo types, refuse everything else
+    private void validatePrimitivesForNull(Object value, Class<?> valType) {
+        if (PRIMITIVES_NOT_ALLOWING_NULL_VAL.contains(valType) && value == null) {
+            // illegal value for primitive type. according to JLS spec they all
+            // have default values and are never null.
+            throw new IllegalStateException( valType + " primitive" +
+                                            " does not accept a null value!");
+        }
+    }
+
+    // Allow valid classes + Pojo types, refuse everything else
     private void validateSaneClassName(Class<?> clazz) {
-        if (WRAPPER_CLASSES.contains(clazz) ||
-                String.class == clazz ||
+        // isAssignableFrom throws NPE if clazz is null.
+        if (VALID_CLASSES.contains(clazz) ||
                 Pojo.class.isAssignableFrom(clazz)) {
             return;
         }
--- a/web/common/src/test/java/com/redhat/thermostat/web/common/PreparedParameterSerializerTest.java	Wed Oct 02 18:14:39 2013 +0200
+++ b/web/common/src/test/java/com/redhat/thermostat/web/common/PreparedParameterSerializerTest.java	Wed Oct 02 15:07:46 2013 +0200
@@ -37,7 +37,6 @@
 package com.redhat.thermostat.web.common;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -69,44 +68,34 @@
     @Test
     public void canDeserializeBasic() {
         // String
-        String jsonStr = "{ \"type\": \"java.lang.String\" , \"value\": \"testing\" , \"isArray\": false}";
+        String jsonStr = "{ \"type\": \"java.lang.String\" , \"value\": \"testing\" }";
         PreparedParameter param = gson.fromJson(jsonStr, PreparedParameter.class);
         assertEquals(String.class, param.getType());
         assertEquals("testing", param.getValue());
-        assertFalse(param.isArrayType());
         // Integer
-        jsonStr = "{ \"type\": \"java.lang.Integer\" , \"value\": -1 , \"isArray\": false}";
+        jsonStr = "{ \"type\": \"int\" , \"value\": -1}";
         param = gson.fromJson(jsonStr, PreparedParameter.class);
-        assertEquals(Integer.class, param.getType());
-        assertTrue(param.getValue() instanceof Integer);
+        assertEquals(int.class, param.getType());
         assertEquals(-1, param.getValue());
-        assertFalse(param.isArrayType());
         // Long
-        jsonStr = "{ \"type\": \"java.lang.Long\" , \"value\": -10 , \"isArray\": false}";
+        jsonStr = "{ \"type\": \"long\" , \"value\": -10}";
         param = gson.fromJson(jsonStr, PreparedParameter.class);
-        assertEquals(Long.class, param.getType());
-        assertTrue(param.getValue() instanceof Long);
+        assertEquals(long.class, param.getType());
         assertEquals(-10L, param.getValue());
-        assertFalse(param.isArrayType());
-        jsonStr = "{ \"type\": \"java.lang.Long\" , \"value\": 30000000003 , \"isArray\": false}";
+        jsonStr = "{ \"type\": \"long\" , \"value\": 30000000003}";
         param = gson.fromJson(jsonStr, PreparedParameter.class);
-        assertEquals(Long.class, param.getType());
-        assertTrue(param.getValue() instanceof Long);
+        assertEquals(long.class, param.getType());
         assertEquals(30000000003L, param.getValue());
-        assertFalse(param.isArrayType());
         // Boolean
-        jsonStr = "{ \"type\": \"java.lang.Boolean\" , \"value\": true , \"isArray\": false}";
+        jsonStr = "{ \"type\": \"boolean\" , \"value\": true}";
         param = gson.fromJson(jsonStr, PreparedParameter.class);
-        assertEquals(Boolean.class, param.getType());
-        assertTrue(param.getValue() instanceof Boolean);
+        assertEquals(boolean.class, param.getType());
         assertEquals(true, param.getValue());
-        assertFalse(param.isArrayType());
         // String[]
         String strArrayVal = "[ \"testing1\", \"testing2\", \"3\" ]";
-        jsonStr = "{ \"type\": \"java.lang.String\" , \"value\": " + strArrayVal + " , \"isArray\": true}";
+        jsonStr = "{ \"type\": \"[Ljava.lang.String;\" , \"value\": " + strArrayVal + "}";
         param = gson.fromJson(jsonStr, PreparedParameter.class);
-        assertEquals(String.class, param.getType());
-        assertTrue(param.isArrayType());
+        assertEquals(String[].class, param.getType());
         assertTrue(param.getValue() instanceof String[]);
         String[] vals = (String[])param.getValue();
         assertEquals(3, vals.length);
@@ -116,6 +105,90 @@
     }
     
     @Test
+    public void allowsBooleanListNullDeserialization() {
+        String jsonStr = "{ \"type\": \"[Z\" , \"value\": null}";
+        PreparedParameter p = gson.fromJson(jsonStr, PreparedParameter.class);
+        assertEquals(boolean[].class, p.getType());
+        assertEquals(null, p.getValue());
+    }
+    
+    @Test
+    public void allowsIntListNullDeserialization() {
+        String jsonStr = "{ \"type\": \"[I\" , \"value\": null}";
+        PreparedParameter p = gson.fromJson(jsonStr, PreparedParameter.class);
+        assertEquals(int[].class, p.getType());
+        assertEquals(null, p.getValue());
+    }
+    
+    @Test
+    public void allowsLongListNullDeserialization() {
+        String jsonStr = "{ \"type\": \"[J\" , \"value\": null}";
+        PreparedParameter p = gson.fromJson(jsonStr, PreparedParameter.class);
+        assertEquals(long[].class, p.getType());
+        assertEquals(null, p.getValue());
+    }
+    
+    @Test
+    public void allowsDoubleListNullDeserialization() {
+        String jsonStr = "{ \"type\": \"[D\" , \"value\": null}";
+        PreparedParameter p = gson.fromJson(jsonStr, PreparedParameter.class);
+        assertEquals(double[].class, p.getType());
+        assertEquals(null, p.getValue());
+    }
+    
+    @Test
+    public void allowsStringNullDeserialization() {
+        String jsonStr = "{ \"type\": \"java.lang.String\" , \"value\": null}";
+        PreparedParameter p = gson.fromJson(jsonStr, PreparedParameter.class);
+        assertEquals(String.class, p.getType());
+        assertEquals(null, p.getValue());
+    }
+    
+    @Test
+    public void allowsStringListNullDeserialization() {
+        String jsonStr = "{ \"type\": \"[Ljava.lang.String;\" , \"value\": null}";
+        PreparedParameter p = gson.fromJson(jsonStr, PreparedParameter.class);
+        assertEquals(String[].class, p.getType());
+        assertEquals(null, p.getValue());
+    }
+    
+    @Test
+    public void rejectNullForBooleanPrimitive() {
+        String jsonStr = "{ \"type\": \"boolean\" , \"value\": null}";
+        doPrimitiveNullTest(jsonStr, "boolean");
+    }
+    
+    @Test
+    public void rejectNullForIntPrimitive() {
+        String jsonStr = "{ \"type\": \"int\" , \"value\": null}";
+        doPrimitiveNullTest(jsonStr, "int");
+    }
+    
+    @Test
+    public void rejectNullForDoublePrimitive() {
+        String jsonStr = "{ \"type\": \"double\" , \"value\": null}";
+        doPrimitiveNullTest(jsonStr, "double");
+    }
+    
+    @Test
+    public void rejectNullForLongPrimitive() {
+        String jsonStr = "{ \"type\": \"long\" , \"value\": null}";
+        doPrimitiveNullTest(jsonStr, "long");
+    }
+    
+    private void doPrimitiveNullTest(String jsonStr, String typeName) {
+        try {
+            gson.fromJson(jsonStr, PreparedParameter.class);
+            // The Java language spec does not permit this
+            fail(typeName + " null primitive should not deserialize");
+        } catch (Exception e) {
+            // pass
+            Throwable cause = e.getCause();
+            assertEquals(typeName + " primitive does not accept a null value!", cause.getMessage());
+        }
+    }
+    
+    @Test
     public void failsDeserializationWrongTypeClass() {
         String jsonStr = "{ \"type\": \"java.io.File\" , \"value\": true}";
         try {
@@ -129,41 +202,88 @@
     }
     
     @Test
+    public void failsDeserializationIfStringForInt() {
+        String jsonStr = "{ \"type\": \"int\" , \"value\":\"testing\"}";
+        try {
+            gson.fromJson(jsonStr, PreparedParameter.class);
+            fail("should have failed to serialize");
+        } catch (Exception e) {
+            // pass
+            Throwable cause = e.getCause();
+            assertTrue(cause instanceof NumberFormatException);
+        }
+    }
+    
+    @Test
+    public void failsDeserializationIfBooleanForInt() {
+        String jsonStr = "{ \"type\": \"int\" , \"value\": true}";
+        try {
+            gson.fromJson(jsonStr, PreparedParameter.class);
+            fail("should have failed to serialize");
+        } catch (Exception e) {
+            // pass
+            Throwable cause = e.getCause();
+            assertTrue(cause instanceof IllegalStateException);
+        }
+    }
+    
+    @Test
+    public void failsDeserializationIfIntForIntList() {
+        String jsonStr = "{ \"type\": \"[I\" , \"value\": -1}";
+        try {
+            gson.fromJson(jsonStr, PreparedParameter.class);
+            fail("should have failed to serialize");
+        } catch (Exception e) {
+            // pass
+            Throwable cause = e.getCause();
+            assertTrue(cause instanceof IllegalStateException);
+        }
+    }
+    
+    @Test
+    public void failsDeserializationIfDoubleForInt() {
+        String jsonStr = "{ \"type\": \"int\" , \"value\": -1.3}";
+        try {
+            gson.fromJson(jsonStr, PreparedParameter.class);
+            fail("should have failed to serialize");
+        } catch (Exception e) {
+            // pass
+            Throwable cause = e.getCause();
+            assertTrue(cause instanceof NumberFormatException);
+        }
+    }
+    
+    @Test
     public void canSerializeBasic() {
         // String
-        String expected = "{\"value\":\"testing\",\"type\":\"java.lang.String\",\"isArray\":false}";
+        String expected = "{\"value\":\"testing\",\"type\":\"java.lang.String\"}";
         PreparedParameter param = new PreparedParameter();
         param.setType(String.class);
         param.setValue("testing");
-        param.setArrayType(false);
         String actual = gson.toJson(param);
         assertEquals(expected, actual);
         // Integer
-        expected = "{\"value\":-1,\"type\":\"java.lang.Integer\",\"isArray\":false}";
-        param.setType(Integer.class);
+        expected = "{\"value\":-1,\"type\":\"int\"}";
+        param.setType(int.class);
         param.setValue(-1);
-        param.setArrayType(false);
         actual = gson.toJson(param);
         assertEquals(expected, actual);
         // Long
-        expected = "{\"value\":30000000003,\"type\":\"java.lang.Long\",\"isArray\":false}";
-        param.setType(Long.class);
+        expected = "{\"value\":30000000003,\"type\":\"long\"}";
+        param.setType(long.class);
         param.setValue(30000000003L);
-        param.setArrayType(false);
         actual = gson.toJson(param);
         assertEquals(expected, actual);
         // boolean
-        expected = "{\"value\":true,\"type\":\"java.lang.Boolean\",\"isArray\":false}";
-        param.setType(Boolean.class);
+        expected = "{\"value\":true,\"type\":\"boolean\"}";
+        param.setType(boolean.class);
         param.setValue(true);
-        param.setArrayType(false);
         actual = gson.toJson(param);
         assertEquals(expected, actual);
         // String[]
         String strArrayVal = "[\"testing1\",\"testing2\",\"3\"]";
-        expected = "{\"value\":" + strArrayVal + ",\"type\":\"java.lang.String\",\"isArray\":true}";
-        param.setType(String.class);
-        param.setArrayType(true);
+        expected = "{\"value\":" + strArrayVal + ",\"type\":\"[Ljava.lang.String;\"}";
+        param.setType(String[].class);
         String[] array = new String[] {
                 "testing1", "testing2", "3"
         };
@@ -175,9 +295,8 @@
     @Test
     public void canSerializeDeserializeInteger() {
         PreparedParameter expected = new PreparedParameter();
-        expected.setType(Integer.class);
+        expected.setType(int.class);
         expected.setValue(3);
-        expected.setArrayType(false);
         String jsonStr = gson.toJson(expected, PreparedParameter.class);
         assertParameterEquals(expected, jsonStr);
     }
@@ -185,13 +304,8 @@
     @Test
     public void canSerializeDeserializeIntegerArray() {
         PreparedParameter expected = new PreparedParameter();
-        expected.setType(Integer.class);
-        // it's important for the expected type to be of primitive array type,
-        // rather than Integer[]. we want the serializer to deserialize to
-        // primitive types if possible. asserted in method assertParameterEquals()
-        // Note that model classes use primitive array types as well.
+        expected.setType(int[].class);
         expected.setValue(new int[] { 0, 3, 20 });
-        expected.setArrayType(true);
         String jsonStr = gson.toJson(expected, PreparedParameter.class);
         assertParameterEquals(expected, jsonStr);
     }
@@ -199,9 +313,8 @@
     @Test
     public void canSerializeDeserializeDouble() {
         PreparedParameter expected = new PreparedParameter();
-        expected.setType(Double.class);
+        expected.setType(double.class);
         expected.setValue(Math.E);
-        expected.setArrayType(false);
         String jsonStr = gson.toJson(expected, PreparedParameter.class);
         assertParameterEquals(expected, jsonStr);
     }
@@ -209,13 +322,8 @@
     @Test
     public void canSerializeDeserializeDoubleArray() {
         PreparedParameter expected = new PreparedParameter();
-        expected.setType(Double.class);
-        // it's important for the expected type to be of primitive array type,
-        // rather than Double[]. we want the serializer to deserialize to
-        // primitive types if possible. asserted in method assertParameterEquals()
-        // Note that model classes use primitive array types as well.
+        expected.setType(double[].class);
         expected.setValue(new double[] { 3.3, 1.0, Math.PI });
-        expected.setArrayType(true);
         String jsonStr = gson.toJson(expected, PreparedParameter.class);
         assertParameterEquals(expected, jsonStr);
     }
@@ -223,9 +331,8 @@
     @Test
     public void canSerializeDeserializeLong() {
         PreparedParameter expected = new PreparedParameter();
-        expected.setType(Long.class);
+        expected.setType(long.class);
         expected.setValue(30000000003L);
-        expected.setArrayType(false);
         String jsonStr = gson.toJson(expected, PreparedParameter.class);
         assertParameterEquals(expected, jsonStr);
     }
@@ -233,13 +340,8 @@
     @Test
     public void canSerializeDeserializeLongArray() {
         PreparedParameter expected = new PreparedParameter();
-        expected.setType(Long.class);
-        // it's important for the expected type to be of primitive array type,
-        // rather than Long[]. we want the serializer to deserialize to
-        // primitive types if possible. asserted in method assertParameterEquals()
-        // Note that model classes use primitive array types as well.
+        expected.setType(long[].class);
         expected.setValue(new long[] { 3000000000L, 3, 20 });
-        expected.setArrayType(true);
         String jsonStr = gson.toJson(expected, PreparedParameter.class);
         assertParameterEquals(expected, jsonStr);
     }
@@ -249,7 +351,6 @@
         PreparedParameter expected = new PreparedParameter();
         expected.setType(String.class);
         expected.setValue("testing");
-        expected.setArrayType(false);
         String jsonStr = gson.toJson(expected, PreparedParameter.class);
         assertParameterEquals(expected, jsonStr);
     }
@@ -257,8 +358,7 @@
     @Test
     public void canSerializeDeserializeStringArray() {
         PreparedParameter expected = new PreparedParameter();
-        expected.setType(String.class);
-        expected.setArrayType(true);
+        expected.setType(String[].class);
         String[] expectedArray = new String[] {
                 "one", "two", "three"      
         };
@@ -270,13 +370,13 @@
     @Test
     public void canSerializeDeserializeBoolean() {
         PreparedParameter expected = new PreparedParameter();
-        expected.setType(Boolean.class);
+        expected.setType(boolean.class);
         expected.setValue(false);
         String jsonStr = gson.toJson(expected, PreparedParameter.class);
         assertParameterEquals(expected, jsonStr);
         
         expected = new PreparedParameter();
-        expected.setType(Boolean.class);
+        expected.setType(boolean.class);
         expected.setValue(true);
         jsonStr = gson.toJson(expected, PreparedParameter.class);
         assertParameterEquals(expected, jsonStr);
@@ -285,13 +385,8 @@
     @Test
     public void canSerializeDeserializeBooleanArray() {
         PreparedParameter expected = new PreparedParameter();
-        expected.setType(Boolean.class);
-        // it's important for the expected type to be of primitive array type,
-        // rather than Boolean[]. we want the serializer to deserialize to
-        // primitive types if possible. asserted in method assertParameterEquals()
-        // Note that model classes use primitive array types as well.
+        expected.setType(boolean[].class);
         expected.setValue(new boolean[] { true, false, false, true });
-        expected.setArrayType(true);
         String jsonStr = gson.toJson(expected, PreparedParameter.class);
         assertParameterEquals(expected, jsonStr);
     }
@@ -303,7 +398,6 @@
         AgentInformation info = new AgentInformation("foo-writer");
         expected.setType(info.getClass());
         expected.setValue(info);
-        expected.setArrayType(false);
         String jsonStr = gson.toJson(expected, PreparedParameter.class);
         assertParameterEquals(expected, jsonStr);
         
@@ -315,7 +409,13 @@
         expected = new PreparedParameter();
         expected.setType(info.getClass());
         expected.setValue(info);
-        expected.setArrayType(false);
+        jsonStr = gson.toJson(expected, PreparedParameter.class);
+        assertParameterEquals(expected, jsonStr);
+        
+        // null pojo
+        expected = new PreparedParameter();
+        expected.setType(AgentInformation.class);
+        expected.setValue(null);
         jsonStr = gson.toJson(expected, PreparedParameter.class);
         assertParameterEquals(expected, jsonStr);
     }
@@ -328,13 +428,11 @@
         pair.setValue("bar");
         expected.setType(pair.getClass());
         expected.setValue(pair);
-        expected.setArrayType(false);
         String jsonStr = gson.toJson(expected, PreparedParameter.class);
         
         PreparedParameter actual = gson.fromJson(jsonStr, PreparedParameter.class);
         
         assertEquals(expected.getType(), actual.getType());
-        assertTrue(expected.isArrayType() == actual.isArrayType());
         assertTrue(actual.getValue() instanceof KeyValuePair);
         KeyValuePair actualPair = (KeyValuePair)actual.getValue();
         assertEquals(pair.getKey(), actualPair.getKey());
@@ -353,7 +451,6 @@
                 info1, info2
         };
         PreparedParameter param = new PreparedParameter();
-        param.setArrayType(true);
         param.setType(AgentInformation.class);
         param.setValue(infos);
         String jsonStr = gson.toJson(param, PreparedParameter.class);
@@ -364,17 +461,15 @@
             String jsonStr) {
         PreparedParameter actual = gson.fromJson(jsonStr, PreparedParameter.class);
         assertEquals(expected.getType(), actual.getType());
-        assertEquals(expected.isArrayType(), actual.isArrayType());
-        if (actual.isArrayType()) {
+        if (actual.getValue() != null && actual.getValue().getClass().isArray()) {
             // compare element by element
             Object values = actual.getValue();
             Object expectedVals = expected.getValue();
-            Class<?> expectedType = expectedVals.getClass();
             int expectedLength = Array.getLength(expectedVals);
             int actualLength = Array.getLength(values);
             assertEquals(expectedLength, actualLength);
             // Make sure the deserialized array is of the correct expected type
-            assertTrue(values.getClass() == expectedType);
+            assertEquals(expectedVals.getClass(), values.getClass());
             for (int i = 0; i < expectedLength; i++) {
                 Object exp = Array.get(expectedVals, i);
                 Object act = Array.get(values, i);
--- a/web/common/src/test/java/com/redhat/thermostat/web/common/PreparedParametersSerializerTest.java	Wed Oct 02 18:14:39 2013 +0200
+++ b/web/common/src/test/java/com/redhat/thermostat/web/common/PreparedParametersSerializerTest.java	Wed Oct 02 15:07:46 2013 +0200
@@ -37,6 +37,7 @@
 package com.redhat.thermostat.web.common;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -121,11 +122,9 @@
         PreparedParameter param1 = actual[0];
         assertEquals("foo-param", param1.getValue());
         assertEquals(String.class, param1.getType());
-        assertEquals(false, param1.isArrayType());
         
         PreparedParameter param2 = actual[1];
-        assertEquals(Long.class, param2.getType());
-        assertEquals(true, param2.isArrayType());
+        assertEquals(long[].class, param2.getType());
         long[] twoActuals = (long[])param2.getValue();
         assertEquals(3, twoActuals.length);
         assertEquals(3000000000L, (long)twoActuals[0]);
@@ -134,7 +133,7 @@
         
         PreparedParameter param3 = actual[2];
         assertEquals(AgentInformation.class, param3.getType());
-        assertEquals(true, param3.isArrayType());
+        assertTrue(param3.getValue().getClass().isArray());
         Pojo[] pojos = (Pojo[])param3.getValue();
         assertEquals(2, pojos.length);
         for (int i = 0; i < pojos.length; i++) {
--- a/web/common/src/test/java/com/redhat/thermostat/web/common/WebPreparedStatementSerializerTest.java	Wed Oct 02 18:14:39 2013 +0200
+++ b/web/common/src/test/java/com/redhat/thermostat/web/common/WebPreparedStatementSerializerTest.java	Wed Oct 02 15:07:46 2013 +0200
@@ -37,9 +37,7 @@
 package com.redhat.thermostat.web.common;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -83,22 +81,18 @@
         PreparedParameter[] parameters = newParams.getParams();
         assertEquals(5, parameters.length);
         assertEquals(2, parameters[0].getValue());
-        assertEquals(Integer.class, parameters[0].getType());
+        assertEquals(int.class, parameters[0].getType());
         assertEquals("testing", parameters[1].getValue());
         assertEquals(String.class, parameters[1].getType());
-        assertFalse(parameters[1].isArrayType());
         assertEquals(222L, parameters[2].getValue());
-        assertEquals(Long.class, parameters[2].getType());
-        assertFalse(parameters[2].isArrayType());
+        assertEquals(long.class, parameters[2].getType());
         String[] list = (String[])parameters[3].getValue();
         assertEquals(2, list.length);
         assertEquals("one", list[0]);
         assertEquals("two", list[1]);
-        assertEquals(String.class, parameters[3].getType());
-        assertTrue(parameters[3].isArrayType());
+        assertEquals(String[].class, parameters[3].getType());
         assertEquals(true, parameters[4].getValue());
-        assertEquals(Boolean.class, parameters[4].getType());
-        assertFalse(parameters[4].isArrayType());
+        assertEquals(boolean.class, parameters[4].getType());
         assertEquals(WebPreparedStatementResponse.DESCRIPTOR_PARSE_FAILED, newStmt.getStatementId());
     }