Mercurial > hg > release > thermostat-1.2
changeset 1515:1744e42fc8b5
Rewrite JSON serializers using com.google.gson.stream API.
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2014-August/010582.html
Reviewed-by: omajid, vanaltj, neugens
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/test/src/main/java/com/redhat/thermostat/testutils/PerformanceTest.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,46 @@ +/* + * Copyright 2012-2014 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.testutils; + +/** + * + * JUnit category class for performance tests. + * + */ +public interface PerformanceTest { + // empty +}
--- a/pom.xml Tue Sep 30 11:57:25 2014 +0200 +++ b/pom.xml Wed Sep 17 11:36:58 2014 +0200 @@ -48,10 +48,22 @@ <url>${thermostat.url}</url> <profiles> + <!-- Profile for running performance tests. Performance tests are + excluded from normal builds. That is, they'll only run if + explicitly requested via -Pperf-tests. See web/common/pom.xml + for an example as to how this property is used. --> + <profile> + <id>perf-tests</id> + <properties> + <!-- define it empty so as to NOT exclude the PerformanceTests + categorized tests. --> + <surefire-perftests-exclusion /> + </properties> + </profile> <!-- Development settings for web.xml. Release builds should have the "environment.type=release" property. --> <profile> - <id>dev-build</id> + <id>dev-build</id> <activation> <property> <name>environment.type</name> @@ -261,6 +273,8 @@ <dev.users.snippet /> <!-- intentionally empty --> <dev.roles.snippet /> <!-- intentionally empty --> <agent.auth.snippet /> <!-- intentionally empty --> + <!-- see web/common/pom.xml for an example as to how this is used --> + <surefire-perftests-exclusion>com.redhat.thermostat.testutils.PerformanceTest</surefire-perftests-exclusion> </properties> <modules>
--- a/web/client/src/main/java/com/redhat/thermostat/web/client/internal/WebStorage.java Tue Sep 30 11:57:25 2014 +0200 +++ b/web/client/src/main/java/com/redhat/thermostat/web/client/internal/WebStorage.java Wed Sep 17 11:36:58 2014 +0200 @@ -67,7 +67,6 @@ import org.apache.http.auth.AuthScope; import org.apache.http.auth.Credentials; import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.CredentialsProvider; import org.apache.http.client.HttpClient; import org.apache.http.client.config.AuthSchemes; import org.apache.http.client.config.RequestConfig; @@ -82,10 +81,8 @@ import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.entity.mime.content.InputStreamBody; -import org.apache.http.impl.auth.BasicScheme; import org.apache.http.impl.auth.BasicSchemeFactory; import org.apache.http.impl.client.BasicCredentialsProvider; -import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; @@ -105,7 +102,6 @@ import com.redhat.thermostat.storage.core.DescriptorParsingException; import com.redhat.thermostat.storage.core.IllegalDescriptorException; import com.redhat.thermostat.storage.core.IllegalPatchException; -import com.redhat.thermostat.storage.core.PreparedParameter; import com.redhat.thermostat.storage.core.PreparedStatement; import com.redhat.thermostat.storage.core.SecureStorage; import com.redhat.thermostat.storage.core.StatementDescriptor; @@ -114,14 +110,16 @@ import com.redhat.thermostat.storage.core.StorageCredentials; import com.redhat.thermostat.storage.core.StorageException; import com.redhat.thermostat.storage.model.Pojo; -import com.redhat.thermostat.web.common.PreparedParameterSerializer; import com.redhat.thermostat.web.common.PreparedStatementResponseCode; -import com.redhat.thermostat.web.common.ThermostatGSONConverter; import com.redhat.thermostat.web.common.WebPreparedStatement; import com.redhat.thermostat.web.common.WebPreparedStatementResponse; -import com.redhat.thermostat.web.common.WebPreparedStatementSerializer; import com.redhat.thermostat.web.common.WebQueryResponse; -import com.redhat.thermostat.web.common.WebQueryResponseSerializer; +import com.redhat.thermostat.web.common.typeadapters.PojoTypeAdapterFactory; +import com.redhat.thermostat.web.common.typeadapters.PreparedParameterTypeAdapterFactory; +import com.redhat.thermostat.web.common.typeadapters.PreparedParametersTypeAdapterFactory; +import com.redhat.thermostat.web.common.typeadapters.WebPreparedStatementResponseTypeAdapterFactory; +import com.redhat.thermostat.web.common.typeadapters.WebPreparedStatementTypeAdapterFactory; +import com.redhat.thermostat.web.common.typeadapters.WebQueryResponseTypeAdapterFactory; public class WebStorage implements Storage, SecureStorage { @@ -380,11 +378,13 @@ private void init(String url, StorageCredentials creds, HttpClient client) { categoryIds = new HashMap<>(); - gson = new GsonBuilder().registerTypeHierarchyAdapter(Pojo.class, - new ThermostatGSONConverter()) - .registerTypeAdapter(WebPreparedStatement.class, new WebPreparedStatementSerializer()) - .registerTypeAdapter(WebQueryResponse.class, new WebQueryResponseSerializer<>()) - .registerTypeAdapter(PreparedParameter.class, new PreparedParameterSerializer()) + gson = new GsonBuilder() + .registerTypeAdapterFactory(new PojoTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementResponseTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebQueryResponseTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParameterTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParametersTypeAdapterFactory()) .create(); httpClient = client; synchronized (httpClientContextLock) {
--- a/web/client/src/test/java/com/redhat/thermostat/web/client/internal/WebStorageTest.java Tue Sep 30 11:57:25 2014 +0200 +++ b/web/client/src/test/java/com/redhat/thermostat/web/client/internal/WebStorageTest.java Wed Sep 17 11:36:58 2014 +0200 @@ -43,9 +43,9 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static org.mockito.Matchers.any; import static org.mockito.Mockito.verifyNoMoreInteractions; import java.io.BufferedReader; @@ -96,7 +96,6 @@ import com.redhat.thermostat.storage.core.DescriptorParsingException; import com.redhat.thermostat.storage.core.IllegalDescriptorException; import com.redhat.thermostat.storage.core.Key; -import com.redhat.thermostat.storage.core.PreparedParameter; import com.redhat.thermostat.storage.core.PreparedParameters; import com.redhat.thermostat.storage.core.PreparedStatement; import com.redhat.thermostat.storage.core.StatementDescriptor; @@ -106,14 +105,15 @@ import com.redhat.thermostat.storage.model.Pojo; import com.redhat.thermostat.test.FreePortFinder; import com.redhat.thermostat.test.FreePortFinder.TryPort; -import com.redhat.thermostat.web.common.PreparedParameterSerializer; import com.redhat.thermostat.web.common.PreparedStatementResponseCode; -import com.redhat.thermostat.web.common.ThermostatGSONConverter; import com.redhat.thermostat.web.common.WebPreparedStatement; import com.redhat.thermostat.web.common.WebPreparedStatementResponse; -import com.redhat.thermostat.web.common.WebPreparedStatementSerializer; import com.redhat.thermostat.web.common.WebQueryResponse; -import com.redhat.thermostat.web.common.WebQueryResponseSerializer; +import com.redhat.thermostat.web.common.typeadapters.PojoTypeAdapterFactory; +import com.redhat.thermostat.web.common.typeadapters.PreparedParameterTypeAdapterFactory; +import com.redhat.thermostat.web.common.typeadapters.WebPreparedStatementResponseTypeAdapterFactory; +import com.redhat.thermostat.web.common.typeadapters.WebPreparedStatementTypeAdapterFactory; +import com.redhat.thermostat.web.common.typeadapters.WebQueryResponseTypeAdapterFactory; public class WebStorageTest { @@ -263,7 +263,8 @@ @Test public void preparingFaultyDescriptorThrowsException() throws UnsupportedEncodingException, IOException { Gson gson = new GsonBuilder() - .registerTypeAdapter(WebPreparedStatement.class, new WebPreparedStatementSerializer()) + .registerTypeAdapterFactory(new WebPreparedStatementTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementResponseTypeAdapterFactory()) .create(); // missing quotes for LHS key @@ -287,7 +288,8 @@ @Test public void preparingUnknownDescriptorThrowsException() throws UnsupportedEncodingException, IOException { Gson gson = new GsonBuilder() - .registerTypeAdapter(WebPreparedStatement.class, new WebPreparedStatementSerializer()) + .registerTypeAdapterFactory(new WebPreparedStatementTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementResponseTypeAdapterFactory()) .create(); String strDesc = "QUERY test WHERE 'property1' = ?s"; @@ -310,11 +312,13 @@ @Test public void forbiddenExecuteQueryThrowsConsumingExcptn() throws UnsupportedEncodingException, IOException { - Gson gson = new GsonBuilder().registerTypeHierarchyAdapter(PreparedParameter.class, new PreparedParameterSerializer()) - .registerTypeAdapter(WebPreparedStatement.class, new WebPreparedStatementSerializer()) - .registerTypeAdapter(WebQueryResponse.class, new WebQueryResponseSerializer<>()) - .registerTypeAdapter(Pojo.class, new ThermostatGSONConverter()) - .create(); + Gson gson = new GsonBuilder() + .registerTypeAdapterFactory(new PojoTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementResponseTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebQueryResponseTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParameterTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementTypeAdapterFactory()) + .create(); String strDesc = "QUERY test WHERE 'property1' = ?s"; StatementDescriptor<TestObj> desc = new StatementDescriptor<>(category, strDesc); @@ -396,11 +400,13 @@ obj1.setProperty1("fluffor1"); TestObj obj2 = new TestObj(); obj2.setProperty1("fluffor2"); - Gson gson = new GsonBuilder().registerTypeHierarchyAdapter(PreparedParameter.class, new PreparedParameterSerializer()) - .registerTypeAdapter(WebPreparedStatement.class, new WebPreparedStatementSerializer()) - .registerTypeAdapter(WebQueryResponse.class, new WebQueryResponseSerializer<>()) - .registerTypeAdapter(Pojo.class, new ThermostatGSONConverter()) - .create(); + Gson gson = new GsonBuilder() + .registerTypeAdapterFactory(new PojoTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementResponseTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebQueryResponseTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParameterTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementTypeAdapterFactory()) + .create(); String strDesc = "QUERY test WHERE 'property1' = ?s"; StatementDescriptor<TestObj> desc = new StatementDescriptor<>(category, strDesc); @@ -462,10 +468,13 @@ obj1.setProperty1("fluffor1"); TestObj obj2 = new TestObj(); obj2.setProperty1("fluffor2"); - Gson gson = new GsonBuilder().registerTypeAdapter(PreparedParameter.class, new PreparedParameterSerializer()) - .registerTypeAdapter(WebPreparedStatement.class, new WebPreparedStatementSerializer()) - .registerTypeHierarchyAdapter(Pojo.class, new ThermostatGSONConverter()) - .create(); + Gson gson = new GsonBuilder() + .registerTypeAdapterFactory(new PojoTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementResponseTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebQueryResponseTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParameterTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementTypeAdapterFactory()) + .create(); String strDesc = "ADD test SET 'property1' = ?s"; StatementDescriptor<TestObj> desc = new StatementDescriptor<>(category, strDesc); @@ -510,10 +519,13 @@ @Test public void forbiddenExecuteWriteReturnsGenericWriteFailure() { - Gson gson = new GsonBuilder().registerTypeAdapter(PreparedParameter.class, new PreparedParameterSerializer()) - .registerTypeAdapter(WebPreparedStatement.class, new WebPreparedStatementSerializer()) - .registerTypeHierarchyAdapter(Pojo.class, new ThermostatGSONConverter()) - .create(); + Gson gson = new GsonBuilder() + .registerTypeAdapterFactory(new PojoTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementResponseTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebQueryResponseTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParameterTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementTypeAdapterFactory()) + .create(); String strDesc = "ADD test SET 'property1' = ?s"; StatementDescriptor<TestObj> desc = new StatementDescriptor<>(category, strDesc);
--- a/web/common/pom.xml Tue Sep 30 11:57:25 2014 +0200 +++ b/web/common/pom.xml Wed Sep 17 11:36:58 2014 +0200 @@ -57,7 +57,12 @@ <artifactId>junit</artifactId> <scope>test</scope> </dependency> - + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-common-test</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> @@ -90,13 +95,21 @@ <Bundle-SymbolicName>com.redhat.thermostat.web.common</Bundle-SymbolicName> <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor> <Export-Package> - com.redhat.thermostat.web.common + com.redhat.thermostat.web.common, + com.redhat.thermostat.web.common.typeadapters, </Export-Package> <!-- Do not autogenerate uses clauses in Manifests --> <_nouses>true</_nouses> </instructions> </configuration> </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <excludedGroups>${surefire-perftests-exclusion}</excludedGroups> + </configuration> + </plugin> </plugins> </build>
--- a/web/common/src/main/java/com/redhat/thermostat/web/common/PreparedParameterSerializer.java Tue Sep 30 11:57:25 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,193 +0,0 @@ -/* - * Copyright 2012-2014 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 java.lang.reflect.Array; -import java.lang.reflect.Type; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; -import com.redhat.thermostat.common.utils.LoggingUtils; -import com.redhat.thermostat.storage.core.PreparedParameter; -import com.redhat.thermostat.storage.model.Pojo; - -/** - * GSON type adapter for {@link PreparedParameter}. - * - */ -public class PreparedParameterSerializer implements JsonDeserializer<PreparedParameter>, JsonSerializer<PreparedParameter>{ - - private static final Logger logger = LoggingUtils.getLogger(PreparedParameterSerializer.class); - private static final String PROP_TYPE = "type"; - private static final String PROP_VALUE = "value"; - 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 { - 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()); - result.add(PROP_VALUE, valueElem); - JsonPrimitive typeElem = new JsonPrimitive(param.getType().getName()); - result.add(PROP_TYPE, typeElem); - return result; - } - - private JsonElement serializeValue(JsonSerializationContext ctxt, Object value, Class<?> type) { - JsonElement element; - // 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, type); - } - return element; - } - - @Override - public PreparedParameter deserialize(JsonElement jsonElem, Type type, - JsonDeserializationContext ctxt) throws JsonParseException { - JsonElement typeElem = jsonElem.getAsJsonObject().get(PROP_TYPE); - String className = typeElem.getAsString(); - // perform some sanity checking on the types of classes we actually - // de-serialize - Class<?> typeVal = deserializeTypeVal(className); - validateSaneClassName(typeVal); - JsonElement valueElement = jsonElem.getAsJsonObject().get(PROP_VALUE); - Object value = deserializeValue(ctxt, valueElement, typeVal); - PreparedParameter param = new PreparedParameter(); - param.setType(typeVal); - param.setValue(value); - return param; - } - - private Class<?> deserializeTypeVal(String className) { - Class<?> typeVal = null; - 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) { - // 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(); - return ctxt.deserialize(valueElement, arrayType); - } else { - Object value = ctxt.deserialize(valueElement, valType); - validatePrimitivesForNull(value, valType); - return value; - } - } - - 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) { - // isAssignableFrom throws NPE if clazz is null. - if (VALID_CLASSES.contains(clazz) || - Pojo.class.isAssignableFrom(clazz)) { - return; - } - throw new IllegalStateException("Illegal type of parameter " + clazz.getCanonicalName()); - } - -} -
--- a/web/common/src/main/java/com/redhat/thermostat/web/common/ThermostatGSONConverter.java Tue Sep 30 11:57:25 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,111 +0,0 @@ -/* - * Copyright 2012-2014 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 java.beans.PropertyDescriptor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Type; - -import org.apache.commons.beanutils.PropertyUtils; - -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; -import com.redhat.thermostat.storage.core.Entity; -import com.redhat.thermostat.storage.core.Persist; -import com.redhat.thermostat.storage.model.Pojo; - -public class ThermostatGSONConverter implements JsonSerializer<Pojo>, JsonDeserializer<Pojo> { - - @Override - public Pojo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - @SuppressWarnings("unchecked") - Class<? extends Pojo> targetType = (Class<Pojo>) typeOfT; - try { - Pojo pojo = targetType.newInstance(); - PropertyDescriptor[] descs = PropertyUtils.getPropertyDescriptors(pojo); - for (PropertyDescriptor desc : descs) { - Method writeMethod = desc.getWriteMethod(); - if (writeMethod != null && writeMethod.isAnnotationPresent(Persist.class)) { - String name = desc.getName(); - JsonElement child = json.getAsJsonObject().get(name); - Object value = context.deserialize(child, desc.getPropertyType()); - PropertyUtils.setProperty(pojo, name, value); - } - } - return pojo; - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - - @Override - public JsonElement serialize(Pojo src, Type typeOfSrc, JsonSerializationContext context) { - Class<?> cls = (Class<?>) typeOfSrc; - if (! cls.isAnnotationPresent(Entity.class)) { - System.err.println("attempt to serialize non-Entity class: " + cls.getName()); - throw new IllegalArgumentException("attempt to serialize non-Entity class: " + cls.getName()); - } - JsonObject obj = new JsonObject(); - PropertyDescriptor[] descs = PropertyUtils.getPropertyDescriptors(src); - for (PropertyDescriptor desc : descs) { - Method readMethod = desc.getReadMethod(); - if (readMethod != null && readMethod.isAnnotationPresent(Persist.class)) { - String name = desc.getName(); - - try { - Object value = PropertyUtils.getProperty(src, name); - obj.add(name, context.serialize(value)); - } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { - throw new RuntimeException(e); - } - } else if (readMethod == null) { - System.err.println("WARNING: property without read method: " + src.getClass().getName() + "::" + desc.getName()); - } - } - return obj; - } - - -} -
--- a/web/common/src/main/java/com/redhat/thermostat/web/common/WebPreparedStatementSerializer.java Tue Sep 30 11:57:25 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,89 +0,0 @@ -/* - * Copyright 2012-2014 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 java.lang.reflect.Type; - -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; -import com.redhat.thermostat.storage.core.PreparedParameters; - -/** - * GSON type adapter for {@link WebPreparedStatement}. - * Depends on {@link PreparedParameterSerializer} being registered as - * type adapter as well. - * - */ -public class WebPreparedStatementSerializer implements - JsonDeserializer<WebPreparedStatement<?>>, - JsonSerializer<WebPreparedStatement<?>> { - - private static final String PROP_PARAMS = "p"; - private static final String PROP_STMT_ID = "sid"; - - @Override - public JsonElement serialize(WebPreparedStatement<?> stmt, Type type, - JsonSerializationContext ctxt) { - JsonObject result = new JsonObject(); - JsonElement parameters = ctxt.serialize(stmt.getParams(), PreparedParameters.class); - result.add(PROP_PARAMS, parameters); - JsonPrimitive stmtIdElem = new JsonPrimitive(stmt.getStatementId()); - result.add(PROP_STMT_ID, stmtIdElem); - return result; - } - - @Override - public WebPreparedStatement<?> deserialize(JsonElement jsonElem, Type type, - JsonDeserializationContext ctxt) throws JsonParseException { - JsonElement paramsElem = jsonElem.getAsJsonObject().get(PROP_PARAMS); - JsonElement stmtIdElem = jsonElem.getAsJsonObject().get(PROP_STMT_ID); - PreparedParameters params = ctxt.deserialize(paramsElem, PreparedParameters.class); - int stmtId = ctxt.deserialize(stmtIdElem, int.class); - WebPreparedStatement<?> stmt = new WebPreparedStatement<>(); - stmt.setStatementId(stmtId); - stmt.setParams(params); - return stmt; - } - -} -
--- a/web/common/src/main/java/com/redhat/thermostat/web/common/WebQueryResponseSerializer.java Tue Sep 30 11:57:25 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,103 +0,0 @@ -/* - * Copyright 2012-2014 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 java.lang.reflect.Array; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; - -import com.google.gson.JsonArray; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; -import com.redhat.thermostat.storage.model.Pojo; - -/** - * GSON type adapter for {@link QueryResponse}. - * Depends on {@link ThermostatGSONConverter} being registered as - * type adapter as well. - * - */ -public class WebQueryResponseSerializer<T extends Pojo> implements - JsonDeserializer<WebQueryResponse<T>>, JsonSerializer<WebQueryResponse<T>> { - - private static final String PROP_RESULT = "payload"; - private static final String PROP_ERROR_CODE = "errno"; - - @Override - public JsonElement serialize(WebQueryResponse<T> qResponse, Type type, - JsonSerializationContext ctxt) { - JsonObject result = new JsonObject(); - JsonElement resultsElem = ctxt.serialize(qResponse.getResultList()); - result.add(PROP_RESULT, resultsElem); - JsonPrimitive errnoElem = new JsonPrimitive(qResponse.getResponseCode()); - result.add(PROP_ERROR_CODE, errnoElem); - return result; - } - - @SuppressWarnings("unchecked") - @Override - public WebQueryResponse<T> deserialize(JsonElement jsonElem, Type type, - JsonDeserializationContext ctxt) throws JsonParseException { - // fromJson() calls need to pass in the right *parameterized* type token: - // example for AgentInformation as T: - // Type queryResponseType = new TypeToken<WebQueryResponse<AgentInformation>>() {}.getType(); - // gson.fromJson(jsonStr, queryResponseType) - Type[] typeParameters = ((ParameterizedType)type).getActualTypeArguments(); - Type queryResponseTypeParam = typeParameters[0]; // WebQueryResponse has only one parameterized type T - JsonArray resultElem = jsonElem.getAsJsonObject().get(PROP_RESULT).getAsJsonArray(); - @SuppressWarnings("rawtypes") - Class typeOfGeneric = (Class)queryResponseTypeParam; - T[] array = (T[])Array.newInstance(typeOfGeneric, resultElem.size()); - for (int i = 0; i < resultElem.size(); i++) { - array[i] = ctxt.deserialize(resultElem.get(i), queryResponseTypeParam); - } - JsonElement errorCodeElem = jsonElem.getAsJsonObject().get(PROP_ERROR_CODE); - int errorCode = ctxt.deserialize(errorCodeElem, int.class); - WebQueryResponse<T> qResponse = new WebQueryResponse<>(); - qResponse.setResponseCode(errorCode); - qResponse.setResultList(array); - return qResponse; - } - -} -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/main/java/com/redhat/thermostat/web/common/typeadapters/PojoTypeAdapter.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,230 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import java.beans.PropertyDescriptor; +import java.io.IOException; +import java.lang.ref.SoftReference; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Objects; + +import org.apache.commons.beanutils.PropertyUtils; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import com.redhat.thermostat.storage.core.Entity; +import com.redhat.thermostat.storage.core.Persist; +import com.redhat.thermostat.storage.model.Pojo; + +/** + * A generic type adapter for types implementing {@link Pojo}. It uses + * no special knowledge of the actual implementation types. + * + */ +class PojoTypeAdapter<T extends Pojo> extends TypeAdapter<Pojo> { + + private final Class<T> runtimeType; + private final Gson gson; + private final TypeAdapterFactory pojoFactory; + private SoftReference<HashMap<String, PropertyDescriptor>> propsCache; + + PojoTypeAdapter(TypeAdapterFactory factory, Gson gson, Class<T> runtimeType) { + assert(Pojo.class.isAssignableFrom(runtimeType)); + this.runtimeType = Objects.requireNonNull(runtimeType); + this.gson = gson; + this.pojoFactory = factory; + // cache will be filled once first used. + this.propsCache = new SoftReference<>(null); + } + + @SuppressWarnings("unchecked") + @Override + public void write(JsonWriter out, Pojo value) throws IOException { + // handle null + if (value == null) { + out.nullValue(); + return; + } + + Class<?> cls = value.getClass(); + if (! cls.isAnnotationPresent(Entity.class)) { + System.err.println("attempt to serialize non-Entity class: " + cls.getName()); + throw new IllegalArgumentException("attempt to serialize non-Entity class: " + cls.getName()); + } + + out.beginObject(); + + PropertyDescriptor[] descs = PropertyUtils.getPropertyDescriptors(value); + for (PropertyDescriptor desc : descs) { + Method readMethod = desc.getReadMethod(); + if (readMethod != null && readMethod.isAnnotationPresent(Persist.class)) { + String name = desc.getName(); + out.name(name); + try { + Object val = PropertyUtils.getProperty(value, name); + if (val == null) { + out.nullValue(); + } else { + // non-null member case + if (Pojo.class.isAssignableFrom(val.getClass())) { + // recursive case + PojoTypeAdapter<T> pojoMemAdapter= new PojoTypeAdapter<>(pojoFactory, gson, (Class<T>)val.getClass()); + pojoMemAdapter.write(out, (T)val); + } else { + // base case: non-pojo type + Class<?> valClass = val.getClass(); + @SuppressWarnings("rawtypes") + TypeToken memberTypeToken = TypeToken.get(valClass); + @SuppressWarnings({"rawtypes"}) + TypeAdapter memberTypeAdapter = getNonPojoTypeAdapter(memberTypeToken); + memberTypeAdapter.write(out, val); + } + } + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + } else if (readMethod == null) { + System.err.println("WARNING: property without read method: " + value.getClass().getName() + "." + desc.getName()); + } + } + + out.endObject(); + } + + @Override + public Pojo read(JsonReader in) throws IOException { + // handle null + JsonToken token = in.peek(); + if (token == JsonToken.NULL) { + return null; + } + + + try { + in.beginObject(); + + Pojo pojo = runtimeType.newInstance(); + // loop over names in JSON + processNamesValues(pojo, in); + + in.endObject(); + + return pojo; + } catch (InstantiationException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + private void processNamesValues(Pojo pojo, JsonReader in) throws IOException { + JsonToken token = in.peek(); + switch(token) { + case NAME: + String name = in.nextName(); + processValue(pojo, in, name); + // recursive case + processNamesValues(pojo, in); + break; + case END_OBJECT: + // Base case + return; + default: + throw new IllegalStateException("Expected NAME or END_OBJECT. Was: " + token); + } + } + + private void processValue(Pojo pojo, JsonReader in, String name) throws IOException { + try { + PropertyDescriptor desc = getPropDescriptor(pojo, name); + if (desc == null) { + throw new IllegalStateException("Property descriptor null for: " + name); + } + Method writeMethod = desc.getWriteMethod(); + Object value = null; + if (writeMethod != null && writeMethod.isAnnotationPresent(Persist.class)) { + Class<?> memberType = desc.getPropertyType(); + if (Pojo.class.isAssignableFrom(memberType)) { + // recursive case + @SuppressWarnings("unchecked") // We've just checked the cast to Class<T> works + PojoTypeAdapter<T> memberAdapter = new PojoTypeAdapter<>(pojoFactory, gson, (Class<T>)memberType); + value = (Pojo)memberAdapter.read(in); + } else { + // base case: non-pojo type + TypeToken<?> memberTypeToken = TypeToken.get(memberType); + TypeAdapter<?> memberTypeAdapter = getNonPojoTypeAdapter(memberTypeToken); + value = memberTypeAdapter.read(in); + } + PropertyUtils.setProperty(pojo, name, value); + } + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + System.err.println("Setter for '" + name + "' not found. The value will be ignored."); + } + } + + private PropertyDescriptor getPropDescriptor(Pojo pojo, String name) { + if (propsCache.get() == null) { + // build cache + HashMap<String, PropertyDescriptor> propDescCache = new HashMap<>(); + PropertyDescriptor[] descs = PropertyUtils.getPropertyDescriptors(pojo); + PropertyDescriptor wanted = null; + for (PropertyDescriptor desc: descs) { + propDescCache.put(desc.getName(), desc); + if (desc.getName().equals(name)) { + wanted = desc; + } + } + propsCache = new SoftReference<>(propDescCache); + return wanted; + } else { + HashMap<String, PropertyDescriptor> cacheMap = propsCache.get(); + return cacheMap.get(name); + } + } + + private <S> TypeAdapter<S> getNonPojoTypeAdapter(TypeToken<S> nonPojoType) { + return gson.getDelegateAdapter(pojoFactory, nonPojoType); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/main/java/com/redhat/thermostat/web/common/typeadapters/PojoTypeAdapterFactory.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,62 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.redhat.thermostat.storage.model.Pojo; + +public class PojoTypeAdapterFactory implements TypeAdapterFactory { + + @Override + public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { + Class<?> rawType = type.getRawType(); + if (Pojo.class.isAssignableFrom(rawType)) { + // We've just verified that we have a Pojo type. The unchecked + // cast is safe. + @SuppressWarnings("unchecked") + Class<T> castedType = (Class<T>)rawType; + @SuppressWarnings({ "unchecked", "rawtypes" }) + TypeAdapter<T> ta = (TypeAdapter<T>)new PojoTypeAdapter(this, gson, castedType); + return ta; + } + return null; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/main/java/com/redhat/thermostat/web/common/typeadapters/PreparedParameterTypeAdapter.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,368 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import java.io.IOException; +import java.lang.reflect.Array; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import com.redhat.thermostat.storage.core.PreparedParameter; +import com.redhat.thermostat.storage.model.Pojo; + +/** + * GSON type adapter for {@link PreparedParameter}. + * + */ +class PreparedParameterTypeAdapter extends TypeAdapter<PreparedParameter> { + + private static final String PROP_TYPE = "type"; + private static final String PROP_VALUE = "value"; + private static final Set<Class<?>> VALID_CLASSES; + private static final Set<Class<?>> PRIMITIVES_NOT_ALLOWING_NULL_VAL; + private static final Set<Class<?>> BASIC_TYPES; + // 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; + + private final Gson gson; + + static { + VALID_CLASSES = new HashSet<>(); + BASIC_TYPES = new HashSet<>(); + BASIC_TYPES.add(int.class); + BASIC_TYPES.add(long.class); + BASIC_TYPES.add(boolean.class); + BASIC_TYPES.add(double.class); + BASIC_TYPES.add(String.class); + 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); + } + + + PreparedParameterTypeAdapter(Gson gson) { + this.gson = gson; + } + + + private Class<?> deserializeTypeVal(String className) { + Class<?> typeVal = null; + 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) { + throw new IllegalStateException("Failed to resolve class type for '" + + className + "'."); + } + } + validateSaneClassName(typeVal); + return typeVal; + } + + 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) { + // isAssignableFrom throws NPE if clazz is null. + if (VALID_CLASSES.contains(clazz) || + Pojo.class.isAssignableFrom(clazz)) { + return; + } + throw new IllegalStateException("Illegal type of parameter " + clazz.getCanonicalName()); + } + + @Override + public PreparedParameter read(JsonReader reader) throws IOException { + if (reader.peek() == JsonToken.NULL) { + reader.nextNull(); + return null; + } + reader.beginObject(); + + Class<?> type = readType(reader); + + assert(type != null); + assert(Pojo.class.isAssignableFrom(type) || VALID_CLASSES.contains(type)); + + Object val = readValue(reader, type); + reader.endObject(); + + PreparedParameter param = new PreparedParameter(); + param.setValue(val); + param.setType(type); + return param; + } + + private Class<?> readType(JsonReader reader) throws IOException { + String name = reader.nextName(); + if (!name.equals(PROP_TYPE)) { + throw new IllegalStateException("Expected " + PROP_VALUE + " but was " + name); + } + String className = reader.nextString(); + return deserializeTypeVal(className); + } + + private Object readValue(JsonReader reader, Class<?> valType) throws IOException { + // values may be null. In that case they are missing from the JSON + // string. Be sure to handle that case early. + if (reader.peek() == JsonToken.END_OBJECT) { + return null; // null parameter value + } + String name = reader.nextName(); + if (!name.equals(PROP_VALUE)) { + throw new IllegalStateException("Expected " + PROP_VALUE + " but was " + name); + } + + JsonToken token = reader.peek(); + if (token == JsonToken.NULL) { + reader.nextNull(); + // Be sure to not allow null values for primitives. Note: + // valType may be an array type for which null is fine. + validatePrimitivesForNull(null, valType); + return null; // null value + } + + // 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 (token == JsonToken.BEGIN_ARRAY && !valType.isArray()) { + assert(Pojo.class.isAssignableFrom(valType)); + Class<?> arrayType = Array.newInstance(valType, 0).getClass(); + TypeAdapter<?> pojoArrayTa = gson.getAdapter(arrayType); + Object val = pojoArrayTa.read(reader); + return val; + } else { + TypeAdapter<?> nonPojoTypeAdapter = gson.getAdapter(valType); + Object val = nonPojoTypeAdapter.read(reader); + validatePrimitivesForNull(val, valType); + return val; + } + } + + + @Override + public void write(JsonWriter writer, PreparedParameter param) + throws IOException { + if (param == null) { + writer.nullValue(); + return; + } + writer.beginObject(); + + // serialize type which must not be null + writer.name(PROP_TYPE); + if (param.getType() == null) { + throw new IllegalStateException("Type of prepared parameter must not be null"); + } + String typeStr = param.getType().getName(); + writer.value(typeStr); + + // serialize value + writer.name(PROP_VALUE); + serializeValue(writer, param); + + writer.endObject(); + } + + private void serializeValue(JsonWriter writer, PreparedParameter param) throws IOException { + if (param.getValue() == null) { + writer.nullValue(); + return; + } + + Object val = param.getValue(); + if (val.getClass().isArray()) { + serializeArrayValue(writer, param); + } else { + serializeBasicValue(writer, param); + } + } + + /* + * Serialize single basic type or Pojo. + */ + private void serializeBasicValue(JsonWriter writer, PreparedParameter param) throws IOException { + assert(param.getValue() != null); + + Class<?> type = param.getType(); + if (BASIC_TYPES.contains(type)) { + serializeSingleBasicValue(writer, param.getValue(), param.getType()); + } else { + assert(Pojo.class.isAssignableFrom(param.getType())); + serializeSinglePojo(writer, param); + } + } + + private void serializeSinglePojo(JsonWriter writer, PreparedParameter param) throws IOException { + assert(param.getValue() != null); + + serializeSinglePojoImpl(writer, param.getValue(), param.getType()); + } + + private void serializeSinglePojoImpl(JsonWriter writer, Object value, Class<?> type) throws IOException { + // precondition(s) + assert(Pojo.class.isAssignableFrom(type)); + assert(value instanceof Pojo); + assert(value != null && !value.getClass().isArray()); + + TypeAdapter<Pojo> pojoTypeAdapter = getTypeAdapter(Pojo.class); + pojoTypeAdapter.write(writer, (Pojo)value); + } + + /* + * Serialize an array of primitives or Pojos + */ + private void serializeArrayValue(JsonWriter writer, + PreparedParameter param) throws IOException { + Class<?> valType = param.getValue().getClass(); + Class<?> typeType = param.getType(); + // Special case pojo list types: the value class is of array type for + // them, but the type is the component type. + if (valType.isArray() && !typeType.isArray()) { + assert(Pojo.class.isAssignableFrom(typeType)); + serializePojoList(writer, param); + } else { + serializeBasicList(writer, param); + } + } + + /* + * Serialize a list of known primitives: boolean, int, long, double, String + */ + private void serializeBasicList(JsonWriter writer, + PreparedParameter param) throws IOException { + // preconditions + assert(param.getValue() != null); + assert(param.getValue().getClass().isArray()); + assert(param.getType().isArray()); + + writer.beginArray(); + int length = Array.getLength(param.getValue()); + for (int i = 0; i < length; i++) { + Object elemVal = Array.get(param.getValue(), i); + Class<?> elemType = param.getType().getComponentType(); + serializeSingleBasicValue(writer, elemVal, elemType); + } + writer.endArray(); + } + + private void serializeSingleBasicValue(JsonWriter writer, Object object, Class<?> type) throws IOException { + assert(!type.isArray()); + if (object == null) { + // must have been a String type + // as other primitive types won't allow null + writer.nullValue(); + return; + } + assert(!object.getClass().isArray()); + assert(BASIC_TYPES.contains(type)); + if (type.isPrimitive()) { + if (type == int.class) { + Integer intVal = (Integer)object; + writer.value(intVal); + } else if (type == long.class) { + writer.value((long)object); + } else if (type == double.class) { + writer.value((double)object); + } else if (type == boolean.class) { + writer.value((boolean)object); + } + } else { + assert(object instanceof String); + writer.value((String)object); + } + } + + /* + * Serialized an array of Pojo and only an array of Pojo + */ + private void serializePojoList(JsonWriter writer, PreparedParameter param) throws IOException { + // preconditions + assert(param.getValue() != null); + assert(Pojo.class.isAssignableFrom(param.getType())); + assert(param.getValue().getClass().isArray()); + + int length = Array.getLength(param.getValue()); + writer.beginArray(); + for (int i = 0; i < length; i++) { + Object value = Array.get(param.getValue(), i); + serializeSinglePojoImpl(writer, value, param.getType()); + } + writer.endArray(); + } + + private <T> TypeAdapter<T> getTypeAdapter(Class<T> typeClass) { + return gson.getAdapter(typeClass); + } + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/main/java/com/redhat/thermostat/web/common/typeadapters/PreparedParameterTypeAdapterFactory.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,59 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.redhat.thermostat.storage.core.PreparedParameter; + +public class PreparedParameterTypeAdapterFactory implements TypeAdapterFactory { + + @Override + public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { + Class<?> rawType = type.getRawType(); + if (rawType == PreparedParameter.class) { + @SuppressWarnings("unchecked") // we've just verified this will work + TypeAdapter<T> ta = (TypeAdapter<T>) new PreparedParameterTypeAdapter(gson); + return ta; + } else { + return null; + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/main/java/com/redhat/thermostat/web/common/typeadapters/PreparedParametersTypeAdapter.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,108 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import java.io.IOException; +import java.lang.reflect.Field; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import com.redhat.thermostat.storage.core.PreparedParameter; +import com.redhat.thermostat.storage.core.PreparedParameters; + +class PreparedParametersTypeAdapter extends TypeAdapter<PreparedParameters> { + + private final Gson gson; + private final Field paramsField; + + PreparedParametersTypeAdapter(Gson gson) { + this.gson = gson; + this.paramsField = getParametersField(); + } + + @Override + public void write(JsonWriter out, PreparedParameters value) + throws IOException { + // handle null + if (value == null) { + out.nullValue(); + return; + } + + TypeAdapter<PreparedParameter[]> paramsTa = gson.getAdapter(PreparedParameter[].class); + paramsTa.write(out, value.getParams()); + } + + @Override + public PreparedParameters read(JsonReader in) throws IOException { + // handle null + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + + TypeAdapter<PreparedParameter[]> paramsTa = gson.getAdapter(PreparedParameter[].class); + PreparedParameters params = new PreparedParameters(0); + PreparedParameter[] payload = paramsTa.read(in); + setParamenters(params, payload); + return params; + } + + private void setParamenters(PreparedParameters params, PreparedParameter[] payload) { + // Set parameters via reflection in order to not be forced to + // introduce a public setter. + try { + paramsField.set(params, payload); + } catch (SecurityException | IllegalArgumentException | IllegalAccessException e) { + throw new IllegalStateException("Failed to set params field"); + } + } + + private Field getParametersField() { + try { + Field field = PreparedParameters.class.getDeclaredField("params"); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException | SecurityException e) { + throw new IllegalStateException("params field not found"); + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/main/java/com/redhat/thermostat/web/common/typeadapters/PreparedParametersTypeAdapterFactory.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,58 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.redhat.thermostat.storage.core.PreparedParameters; + +public class PreparedParametersTypeAdapterFactory implements TypeAdapterFactory { + + @Override + public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { + Class<?> rawType = type.getRawType(); + if (rawType == PreparedParameters.class) { + @SuppressWarnings("unchecked") + TypeAdapter<T> ta = (TypeAdapter<T>)new PreparedParametersTypeAdapter(gson); + return ta; + } + return null; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/main/java/com/redhat/thermostat/web/common/typeadapters/WebPreparedStatementResponseTypeAdapter.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,104 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import java.io.IOException; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import com.redhat.thermostat.web.common.WebPreparedStatementResponse; + +class WebPreparedStatementResponseTypeAdapter extends + TypeAdapter<WebPreparedStatementResponse> { + + private static final String NUM_FREE_VARS_NAME = "numFreeVars"; + private static final String STMT_ID_NAME = "stmtId"; + + WebPreparedStatementResponseTypeAdapter() { + // package-private no-arg constructor + } + + @Override + public WebPreparedStatementResponse read(JsonReader reader) + throws IOException { + if (reader.peek() == JsonToken.NULL) { + reader.nextNull(); + return null; + } + + WebPreparedStatementResponse response = new WebPreparedStatementResponse(); + + reader.beginObject(); + String name = reader.nextName(); + if (name.equals(NUM_FREE_VARS_NAME)) { + response.setNumFreeVariables(reader.nextInt()); + } + name = reader.nextName(); + if (name.equals(STMT_ID_NAME)) { + response.setStatementId(reader.nextInt()); + } + reader.endObject(); + + return response; + } + + @Override + public void write(JsonWriter writer, WebPreparedStatementResponse value) + throws IOException { + if (value == null) { + writer.nullValue(); + return; + } + + int freeVars = value.getNumFreeVariables(); + int stmtId = value.getStatementId(); + + writer.beginObject(); + + // Free variables + writer.name(NUM_FREE_VARS_NAME); + writer.value(freeVars); + // stmt id + writer.name(STMT_ID_NAME); + writer.value(stmtId); + + writer.endObject(); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/main/java/com/redhat/thermostat/web/common/typeadapters/WebPreparedStatementResponseTypeAdapterFactory.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,59 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.redhat.thermostat.web.common.WebPreparedStatementResponse; + +public class WebPreparedStatementResponseTypeAdapterFactory implements + TypeAdapterFactory { + + @Override + public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { + Class<?> rawType = type.getRawType(); + if (rawType == WebPreparedStatementResponse.class) { + @SuppressWarnings("unchecked") + TypeAdapter<T> ta = (TypeAdapter<T>)new WebPreparedStatementResponseTypeAdapter(); + return ta; + } + return null; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/main/java/com/redhat/thermostat/web/common/typeadapters/WebPreparedStatementTypeAdapter.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,122 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import java.io.IOException; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import com.redhat.thermostat.storage.core.PreparedParameters; +import com.redhat.thermostat.web.common.WebPreparedStatement; + +@SuppressWarnings("rawtypes") +class WebPreparedStatementTypeAdapter extends TypeAdapter<WebPreparedStatement> { + + private static final String PROP_PARAMS = "p"; + private static final String PROP_STMT_ID = "sid"; + + private final Gson gson; + + WebPreparedStatementTypeAdapter(Gson gson) { + this.gson = gson; + } + + @Override + public void write(JsonWriter out, WebPreparedStatement value) + throws IOException { + // handle null + if (value == null) { + out.nullValue(); + return; + } + + out.beginObject(); + + // statement id + out.name(PROP_STMT_ID); + out.value(value.getStatementId()); + + // prepared parameters + out.name(PROP_PARAMS); + TypeAdapter<PreparedParameters> ta = gson.getAdapter(PreparedParameters.class); + ta.write(out, value.getParams()); + + out.endObject(); + } + + @Override + public WebPreparedStatement read(JsonReader in) throws IOException { + // handle null + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + + in.beginObject(); + + + // statement id + String name = in.nextName(); + if (!name.equals(PROP_STMT_ID)) { + throw new IllegalStateException("Expected name " + PROP_STMT_ID + " but was " + name); + } + int stmtId = in.nextInt(); + + // params + PreparedParameters params = null; + // params value might be null and missing. + if (in.peek() == JsonToken.NAME) { + name = in.nextName(); + if (!name.equals(PROP_PARAMS)) { + throw new IllegalStateException("Expected name " + PROP_PARAMS + " but was " + name); + } + TypeAdapter<PreparedParameters> ta = gson.getAdapter(PreparedParameters.class); + params = ta.read(in); + } + + in.endObject(); + + WebPreparedStatement<?> stmt = new WebPreparedStatement<>(); + stmt.setParams(params); + stmt.setStatementId(stmtId); + return stmt; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/main/java/com/redhat/thermostat/web/common/typeadapters/WebPreparedStatementTypeAdapterFactory.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,59 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.redhat.thermostat.web.common.WebPreparedStatement; + +public class WebPreparedStatementTypeAdapterFactory implements + TypeAdapterFactory { + + @Override + public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { + Class<?> rawType = type.getRawType(); + if (rawType == WebPreparedStatement.class) { + @SuppressWarnings("unchecked") // We've just checked the raw type + TypeAdapter<T> ta = (TypeAdapter<T>)new WebPreparedStatementTypeAdapter(gson); + return ta; + } + return null; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/main/java/com/redhat/thermostat/web/common/typeadapters/WebQueryResponseTypeAdapter.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,158 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import java.io.IOException; +import java.lang.reflect.Array; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import com.redhat.thermostat.storage.model.Pojo; +import com.redhat.thermostat.web.common.WebQueryResponse; + +class WebQueryResponseTypeAdapter<T extends Pojo> extends TypeAdapter<WebQueryResponse<T>> { + + private static final String PROP_RESULT = "payload"; + private static final String PROP_ERROR_CODE = "errno"; + + // The runtime type of the Pojo + private final Class<T> runtimePojoType; + private final Gson gson; + + WebQueryResponseTypeAdapter(Type parameterizedType, Gson gson) { + this.runtimePojoType = extractPojoImplClassFromType(parameterizedType); + this.gson = gson; + } + + @Override + public void write(JsonWriter out, WebQueryResponse<T> value) + throws IOException { + // handle null + if (value == null) { + out.nullValue(); + return; + } + + out.beginObject(); + + // response code + out.name(PROP_ERROR_CODE); + out.value(value.getResponseCode()); + + // payload + out.name(PROP_RESULT); + @SuppressWarnings("unchecked") + TypeAdapter<T[]> pojoTa = (TypeAdapter<T[]>)gson.getAdapter(value.getResultList().getClass()); + pojoTa.write(out, value.getResultList()); + + out.endObject(); + } + + @Override + public WebQueryResponse<T> read(JsonReader in) throws IOException { + // handle null + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + + in.beginObject(); + + // response code + int responseCode = 0; + String name = in.nextName(); + if (name.equals(PROP_ERROR_CODE)) { + responseCode = in.nextInt(); + } else { + throw new IllegalStateException("Expected " + PROP_ERROR_CODE + " but got " + name); + } + + if (runtimePojoType == null) { + throw new IllegalStateException("Runtime pojo type unknown"); + } + // Result list may be null. If it's there we get a name, otherwise + // we are done deserializing + T[] resultList = null; + if (in.peek() == JsonToken.NAME) { + name = in.nextName(); + if (name.equals(PROP_RESULT)) { + @SuppressWarnings("unchecked") + T[] arrayType = (T[])Array.newInstance(runtimePojoType, 0); + @SuppressWarnings("unchecked") + Class<T[]> type = (Class<T[]>)arrayType.getClass(); + TypeAdapter<T[]> pojoTa = gson.getAdapter(type); + resultList = pojoTa.read(in); + } else { + throw new IllegalStateException("Expected " + PROP_RESULT + " but got " + name); + } + } + + in.endObject(); + + WebQueryResponse<T> qResponse = new WebQueryResponse<>(); + qResponse.setResponseCode(responseCode); + qResponse.setResultList(resultList); + return qResponse; + } + + private Class<T> extractPojoImplClassFromType(Type type) { + if (type instanceof ParameterizedType) { + // fromJson() calls need to pass in the right *parameterized* type token: + // example for AgentInformation as T: + // Type queryResponseType = new TypeToken<WebQueryResponse<AgentInformation>>() {}.getType(); + // gson.fromJson(jsonStr, queryResponseType) + Type[] typeParameters = ((ParameterizedType)type).getActualTypeArguments(); + Type queryResponseTypeParam = typeParameters[0]; // WebQueryResponse has only one parameterized type T + Class<?> pojoClass = (Class<?>)queryResponseTypeParam; + if (!Pojo.class.isAssignableFrom(pojoClass)) { + throw new IllegalStateException("WebQueryResponse parametrized with non-pojo class: " + pojoClass); + } else { + @SuppressWarnings("unchecked") // We've just verified it's Pojo assignable. + Class<T> retval = (Class<T>)pojoClass; + return retval; + } + } + // should-be: write() will be called and only write(). + return null; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/main/java/com/redhat/thermostat/web/common/typeadapters/WebQueryResponseTypeAdapterFactory.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,58 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.redhat.thermostat.web.common.WebQueryResponse; + +public class WebQueryResponseTypeAdapterFactory implements TypeAdapterFactory { + + @Override + public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { + Class<?> rawType = type.getRawType(); + if (rawType == WebQueryResponse.class) { + @SuppressWarnings({ "unchecked", "rawtypes" }) + TypeAdapter<T> ta = (TypeAdapter<T>)new WebQueryResponseTypeAdapter(type.getType(), gson); + return ta; + } + return null; + } + +}
--- a/web/common/src/test/java/com/redhat/thermostat/web/common/PreparedParameterSerializerTest.java Tue Sep 30 11:57:25 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,483 +0,0 @@ -/* - * Copyright 2012-2014 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.assertTrue; -import static org.junit.Assert.fail; - -import java.lang.reflect.Array; - -import org.junit.Before; -import org.junit.Test; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.redhat.thermostat.storage.core.PreparedParameter; -import com.redhat.thermostat.storage.model.AgentInformation; -import com.redhat.thermostat.storage.model.Pojo; -import com.redhat.thermostat.storage.model.VmInfo.KeyValuePair; - -public class PreparedParameterSerializerTest { - - private Gson gson; - - @Before - public void setup() { - gson = new GsonBuilder() - .registerTypeHierarchyAdapter(Pojo.class, new ThermostatGSONConverter()) - .registerTypeAdapter( - PreparedParameter.class, - new PreparedParameterSerializer()).create(); - } - - @Test - public void canDeserializeBasic() { - // String - String jsonStr = "{ \"type\": \"java.lang.String\" , \"value\": \"testing\" }"; - PreparedParameter param = gson.fromJson(jsonStr, PreparedParameter.class); - assertEquals(String.class, param.getType()); - assertEquals("testing", param.getValue()); - // Integer - jsonStr = "{ \"type\": \"int\" , \"value\": -1}"; - param = gson.fromJson(jsonStr, PreparedParameter.class); - assertEquals(int.class, param.getType()); - assertEquals(-1, param.getValue()); - // Long - jsonStr = "{ \"type\": \"long\" , \"value\": -10}"; - param = gson.fromJson(jsonStr, PreparedParameter.class); - assertEquals(long.class, param.getType()); - assertEquals(-10L, param.getValue()); - jsonStr = "{ \"type\": \"long\" , \"value\": 30000000003}"; - param = gson.fromJson(jsonStr, PreparedParameter.class); - assertEquals(long.class, param.getType()); - assertEquals(30000000003L, param.getValue()); - // Boolean - jsonStr = "{ \"type\": \"boolean\" , \"value\": true}"; - param = gson.fromJson(jsonStr, PreparedParameter.class); - assertEquals(boolean.class, param.getType()); - assertEquals(true, param.getValue()); - // String[] - String strArrayVal = "[ \"testing1\", \"testing2\", \"3\" ]"; - jsonStr = "{ \"type\": \"[Ljava.lang.String;\" , \"value\": " + strArrayVal + "}"; - param = gson.fromJson(jsonStr, PreparedParameter.class); - assertEquals(String[].class, param.getType()); - assertTrue(param.getValue() instanceof String[]); - String[] vals = (String[])param.getValue(); - assertEquals(3, vals.length); - assertEquals("testing1", vals[0]); - assertEquals("testing2", vals[1]); - assertEquals("3", vals[2]); - } - - @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 { - gson.fromJson(jsonStr, PreparedParameter.class); - fail("should have failed to serialize"); - } catch (Exception e) { - // pass - Throwable cause = e.getCause(); - assertTrue(cause.getMessage().contains("Illegal type of parameter")); - } - } - - @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\"}"; - PreparedParameter param = new PreparedParameter(); - param.setType(String.class); - param.setValue("testing"); - String actual = gson.toJson(param); - assertEquals(expected, actual); - // Integer - expected = "{\"value\":-1,\"type\":\"int\"}"; - param.setType(int.class); - param.setValue(-1); - actual = gson.toJson(param); - assertEquals(expected, actual); - // Long - expected = "{\"value\":30000000003,\"type\":\"long\"}"; - param.setType(long.class); - param.setValue(30000000003L); - actual = gson.toJson(param); - assertEquals(expected, actual); - // boolean - expected = "{\"value\":true,\"type\":\"boolean\"}"; - param.setType(boolean.class); - param.setValue(true); - actual = gson.toJson(param); - assertEquals(expected, actual); - // String[] - String strArrayVal = "[\"testing1\",\"testing2\",\"3\"]"; - expected = "{\"value\":" + strArrayVal + ",\"type\":\"[Ljava.lang.String;\"}"; - param.setType(String[].class); - String[] array = new String[] { - "testing1", "testing2", "3" - }; - param.setValue(array); - actual = gson.toJson(param); - assertEquals(expected, actual); - } - - @Test - public void canSerializeDeserializeInteger() { - PreparedParameter expected = new PreparedParameter(); - expected.setType(int.class); - expected.setValue(3); - String jsonStr = gson.toJson(expected, PreparedParameter.class); - assertParameterEquals(expected, jsonStr); - } - - @Test - public void canSerializeDeserializeIntegerArray() { - PreparedParameter expected = new PreparedParameter(); - expected.setType(int[].class); - expected.setValue(new int[] { 0, 3, 20 }); - String jsonStr = gson.toJson(expected, PreparedParameter.class); - assertParameterEquals(expected, jsonStr); - } - - @Test - public void canSerializeDeserializeDouble() { - PreparedParameter expected = new PreparedParameter(); - expected.setType(double.class); - expected.setValue(Math.E); - String jsonStr = gson.toJson(expected, PreparedParameter.class); - assertParameterEquals(expected, jsonStr); - } - - @Test - public void canSerializeDeserializeDoubleArray() { - PreparedParameter expected = new PreparedParameter(); - expected.setType(double[].class); - expected.setValue(new double[] { 3.3, 1.0, Math.PI }); - String jsonStr = gson.toJson(expected, PreparedParameter.class); - assertParameterEquals(expected, jsonStr); - } - - @Test - public void canSerializeDeserializeLong() { - PreparedParameter expected = new PreparedParameter(); - expected.setType(long.class); - expected.setValue(30000000003L); - String jsonStr = gson.toJson(expected, PreparedParameter.class); - assertParameterEquals(expected, jsonStr); - } - - @Test - public void canSerializeDeserializeLongArray() { - PreparedParameter expected = new PreparedParameter(); - expected.setType(long[].class); - expected.setValue(new long[] { 3000000000L, 3, 20 }); - String jsonStr = gson.toJson(expected, PreparedParameter.class); - assertParameterEquals(expected, jsonStr); - } - - @Test - public void canSerializeDeserializeString() { - PreparedParameter expected = new PreparedParameter(); - expected.setType(String.class); - expected.setValue("testing"); - String jsonStr = gson.toJson(expected, PreparedParameter.class); - assertParameterEquals(expected, jsonStr); - } - - @Test - public void canSerializeDeserializeStringArray() { - PreparedParameter expected = new PreparedParameter(); - expected.setType(String[].class); - String[] expectedArray = new String[] { - "one", "two", "three" - }; - expected.setValue(expectedArray); - String jsonStr = gson.toJson(expected, PreparedParameter.class); - assertParameterEquals(expected, jsonStr); - } - - @Test - public void canSerializeDeserializeBoolean() { - PreparedParameter expected = new PreparedParameter(); - 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.setValue(true); - jsonStr = gson.toJson(expected, PreparedParameter.class); - assertParameterEquals(expected, jsonStr); - } - - @Test - public void canSerializeDeserializeBooleanArray() { - PreparedParameter expected = new PreparedParameter(); - expected.setType(boolean[].class); - expected.setValue(new boolean[] { true, false, false, true }); - String jsonStr = gson.toJson(expected, PreparedParameter.class); - assertParameterEquals(expected, jsonStr); - } - - - @Test - public void canSerializeDeserializePojos() { - PreparedParameter expected = new PreparedParameter(); - AgentInformation info = new AgentInformation("foo-writer"); - expected.setType(info.getClass()); - expected.setValue(info); - String jsonStr = gson.toJson(expected, PreparedParameter.class); - assertParameterEquals(expected, jsonStr); - - info = new AgentInformation("some-writer"); - info.setAlive(true); - info.setConfigListenAddress("127.0.0.1:12000"); - info.setStartTime(System.currentTimeMillis()); - info.setStopTime(System.currentTimeMillis()); - expected = new PreparedParameter(); - expected.setType(info.getClass()); - expected.setValue(info); - 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); - } - - @Test - public void canSerializeDeserializeInnerClassPojoTypes() { - PreparedParameter expected = new PreparedParameter(); - KeyValuePair pair = new KeyValuePair(); - pair.setKey("foo"); - pair.setValue("bar"); - expected.setType(pair.getClass()); - expected.setValue(pair); - String jsonStr = gson.toJson(expected, PreparedParameter.class); - - PreparedParameter actual = gson.fromJson(jsonStr, PreparedParameter.class); - - assertEquals(expected.getType(), actual.getType()); - assertTrue(actual.getValue() instanceof KeyValuePair); - KeyValuePair actualPair = (KeyValuePair)actual.getValue(); - assertEquals(pair.getKey(), actualPair.getKey()); - assertEquals(pair.getValue(), actualPair.getValue()); - } - - @Test - public void canSerializeDeserializePojoLists() { - AgentInformation info1 = new AgentInformation("foo-writer"); - AgentInformation info2 = new AgentInformation("some-writer"); - info2.setAlive(true); - info2.setConfigListenAddress("127.0.0.1:12000"); - info2.setStartTime(System.currentTimeMillis()); - info2.setStopTime(System.currentTimeMillis()); - AgentInformation[] infos = new AgentInformation[] { - info1, info2 - }; - PreparedParameter param = new PreparedParameter(); - param.setType(AgentInformation.class); - param.setValue(infos); - String jsonStr = gson.toJson(param, PreparedParameter.class); - assertParameterEquals(param, jsonStr); - } - - private void assertParameterEquals(PreparedParameter expected, - String jsonStr) { - PreparedParameter actual = gson.fromJson(jsonStr, PreparedParameter.class); - assertEquals(expected.getType(), actual.getType()); - if (actual.getValue() != null && actual.getValue().getClass().isArray()) { - // compare element by element - Object values = actual.getValue(); - Object expectedVals = expected.getValue(); - int expectedLength = Array.getLength(expectedVals); - int actualLength = Array.getLength(values); - assertEquals(expectedLength, actualLength); - // Make sure the deserialized array is of the correct expected type - assertEquals(expectedVals.getClass(), values.getClass()); - for (int i = 0; i < expectedLength; i++) { - Object exp = Array.get(expectedVals, i); - Object act = Array.get(values, i); - assertEquals(exp, act); - } - } else { - assertEquals(expected.getValue(), actual.getValue()); - } - } -} -
--- a/web/common/src/test/java/com/redhat/thermostat/web/common/PreparedParametersSerializerTest.java Tue Sep 30 11:57:25 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,144 +0,0 @@ -/* - * Copyright 2012-2014 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.assertTrue; - -import org.junit.Before; -import org.junit.Test; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.redhat.thermostat.storage.core.PreparedParameter; -import com.redhat.thermostat.storage.core.PreparedParameters; -import com.redhat.thermostat.storage.model.AgentInformation; -import com.redhat.thermostat.storage.model.Pojo; - -public class PreparedParametersSerializerTest { - - private Gson gson; - - @Before - public void setup() { - gson = new GsonBuilder() - .registerTypeHierarchyAdapter(Pojo.class, - new ThermostatGSONConverter()) - .registerTypeAdapter( - PreparedParameter.class, - new PreparedParameterSerializer()).create(); - } - - @Test - public void canSerializeDeserializeBasic() { - PreparedParameters params = new PreparedParameters(5); - params.setBoolean(0, true); - params.setInt(1, 2300); - params.setLong(2, 2200000000L); - params.setString(3, "testing"); - String[] list = new String[] { - "a", "b", "c" - }; - params.setStringList(4, list); - - String jsonStr = gson.toJson(params, PreparedParameters.class); - PreparedParameters actualParams = gson.fromJson(jsonStr, PreparedParameters.class); - - PreparedParameter[] expected = params.getParams(); - PreparedParameter[] actual = actualParams.getParams(); - - // last element is the string array, which we check manually - for (int i = 0; i < expected.length - 1; i++) { - assertEquals(expected[i].getType(), actual[i].getType()); - assertEquals(expected[i].getValue(), actual[i].getValue()); - } - String actualList[] = (String[])actual[4].getValue(); - for (int i = 0; i < list.length; i++) { - assertEquals(list[i], actualList[i]); - } - } - - @Test - public void canSerializeDeserializeMixedTypesWithPojoList() { - AgentInformation info1 = new AgentInformation("foo-agent"); - info1.setAlive(true); - AgentInformation info2 = new AgentInformation("foo-agent"); - info2.setAlive(false); - info2.setStartTime(System.currentTimeMillis()); - info2.setStopTime(System.currentTimeMillis()); - info2.setConfigListenAddress("127.0.0.1:12000"); - AgentInformation[] infos = new AgentInformation[] { - info1, info2 - }; - long[] longs = new long[] { 3000000000L, -3, 300 }; - // String, long[], Pojo[] - PreparedParameters params = new PreparedParameters(3); - params.setString(0, "foo-param"); - params.setLongList(1, longs); - params.setPojoList(2, infos); - - String jsonStr = gson.toJson(params, PreparedParameters.class); - PreparedParameters actualParams = gson.fromJson(jsonStr, PreparedParameters.class); - - PreparedParameter[] expected = params.getParams(); - PreparedParameter[] actual = actualParams.getParams(); - - assertEquals(expected.length, actual.length); - - PreparedParameter param1 = actual[0]; - assertEquals("foo-param", param1.getValue()); - assertEquals(String.class, param1.getType()); - - PreparedParameter param2 = actual[1]; - assertEquals(long[].class, param2.getType()); - long[] twoActuals = (long[])param2.getValue(); - assertEquals(3, twoActuals.length); - assertEquals(3000000000L, (long)twoActuals[0]); - assertEquals(-3, (long)twoActuals[1]); - assertEquals(300, (long)twoActuals[2]); - - PreparedParameter param3 = actual[2]; - assertEquals(AgentInformation.class, param3.getType()); - assertTrue(param3.getValue().getClass().isArray()); - Pojo[] pojos = (Pojo[])param3.getValue(); - assertEquals(2, pojos.length); - for (int i = 0; i < pojos.length; i++) { - assertEquals(infos[i], pojos[i]); - } - } -} -
--- a/web/common/src/test/java/com/redhat/thermostat/web/common/ThermostatGSONConverterTest.java Tue Sep 30 11:57:25 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,103 +0,0 @@ -/* - * Copyright 2012-2014 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 org.junit.Before; -import org.junit.Test; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.redhat.thermostat.storage.model.AgentInformation; -import com.redhat.thermostat.storage.model.AggregateCount; -import com.redhat.thermostat.storage.model.Pojo; - -public class ThermostatGSONConverterTest { - - private Gson gson; - - @Before - public void setup() { - gson = new GsonBuilder() - .registerTypeAdapter(Pojo.class, new ThermostatGSONConverter()) - .create(); - } - - @Test - public void canSerializeDeserializeBasic() { - // Our test pojo - AgentInformation agentInfo = new AgentInformation("testing"); - agentInfo.setAlive(true); - - String jsonStr = gson.toJson(agentInfo); - - AgentInformation actual = gson.fromJson(jsonStr, AgentInformation.class); - - assertEquals("testing", actual.getAgentId()); - assertEquals(true, actual.isAlive()); - } - - @Test - public void canSerializeDeserializeArray() { - // Our test pojo - AgentInformation agentInfo = new AgentInformation("testing"); - agentInfo.setAlive(true); - AgentInformation[] agentInfos = new AgentInformation[] { - agentInfo - }; - - String jsonStr = gson.toJson(agentInfos); - - AgentInformation[] actual = gson.fromJson(jsonStr, AgentInformation[].class); - - assertEquals("testing", actual[0].getAgentId()); - assertEquals(true, actual[0].isAlive()); - } - - @Test - public void canSerializeDeserializeAggregateCount() { - long expectedCount = 3333000333L; - AggregateCount count = new AggregateCount(); - count.setCount(expectedCount); - String jsonStr = gson.toJson(count); - // now do the reverse - AggregateCount c2 = gson.fromJson(jsonStr, AggregateCount.class); - assertEquals(expectedCount, c2.getCount()); - } -} -
--- a/web/common/src/test/java/com/redhat/thermostat/web/common/WebPreparedStatementResponseSerializerTest.java Tue Sep 30 11:57:25 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,72 +0,0 @@ -/* - * Copyright 2012-2014 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 org.junit.Before; -import org.junit.Test; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -public class WebPreparedStatementResponseSerializerTest { - - private Gson gson; - - @Before - public void setup() { - gson = new GsonBuilder().create(); - } - - @Test - public void testSerializationDeserializationBasic() { - WebPreparedStatementResponse response = new WebPreparedStatementResponse(); - response.setNumFreeVariables(6); - response.setStatementId(WebPreparedStatementResponse.ILLEGAL_STATEMENT); - - String jsonStr = gson.toJson(response, WebPreparedStatementResponse.class); - String expectedString = "{\"numFreeVariables\":6,\"statementId\":-1}"; - assertEquals(expectedString, jsonStr); - - WebPreparedStatementResponse actual = gson.fromJson(jsonStr, WebPreparedStatementResponse.class); - - assertEquals(6, actual.getNumFreeVariables()); - assertEquals(WebPreparedStatementResponse.ILLEGAL_STATEMENT, actual.getStatementId()); - } -} -
--- a/web/common/src/test/java/com/redhat/thermostat/web/common/WebPreparedStatementSerializerTest.java Tue Sep 30 11:57:25 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,126 +0,0 @@ -/* - * Copyright 2012-2014 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.assertNotNull; - -import org.junit.Before; -import org.junit.Test; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.redhat.thermostat.storage.core.PreparedParameter; -import com.redhat.thermostat.storage.core.PreparedParameters; -import com.redhat.thermostat.storage.model.AgentInformation; -import com.redhat.thermostat.storage.model.Pojo; - -public class WebPreparedStatementSerializerTest { - - private Gson gson; - - @Before - public void setup() { - gson = new GsonBuilder() - .registerTypeAdapter(WebPreparedStatement.class, - new WebPreparedStatementSerializer()) - .registerTypeHierarchyAdapter(Pojo.class, new ThermostatGSONConverter()) - .registerTypeAdapter(PreparedParameter.class, - new PreparedParameterSerializer()).create(); - } - - @Test - public void canSerializeAndDeserialize() { - PreparedParameters params = new PreparedParameters(5); - params.setInt(0, 2); - params.setString(1, "testing"); - params.setLong(2, 222L); - params.setStringList(3, new String[] { "one", "two" }); - params.setBoolean(4, true); - WebPreparedStatement<?> stmt = new WebPreparedStatement<>(); - stmt.setParams(params); - stmt.setStatementId(WebPreparedStatementResponse.DESCRIPTOR_PARSE_FAILED); - String jsonString = gson.toJson(stmt, WebPreparedStatement.class); - WebPreparedStatement<?> newStmt = gson.fromJson(jsonString, WebPreparedStatement.class); - assertNotNull(newStmt); - PreparedParameters newParams = newStmt.getParams(); - PreparedParameter[] parameters = newParams.getParams(); - assertEquals(5, parameters.length); - assertEquals(2, parameters[0].getValue()); - assertEquals(int.class, parameters[0].getType()); - assertEquals("testing", parameters[1].getValue()); - assertEquals(String.class, parameters[1].getType()); - assertEquals(222L, parameters[2].getValue()); - 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()); - assertEquals(true, parameters[4].getValue()); - assertEquals(boolean.class, parameters[4].getType()); - assertEquals(WebPreparedStatementResponse.DESCRIPTOR_PARSE_FAILED, newStmt.getStatementId()); - } - - /* - * Writes need Pojo support for serialization. This is a basic test we do - * get Pojos across the wire in a prepared context. - */ - @Test - public void canSerializeDeserializePojoParameters() { - PreparedParameters params = new PreparedParameters(2); - params.setIntList(0, new int[] { 0, 300 }); - AgentInformation pojo1 = new AgentInformation("foo-agent"); - AgentInformation pojo2 = new AgentInformation("foo-agent"); - pojo2.setAlive(true); - pojo2.setConfigListenAddress("127.0.0.1:38822"); - params.setPojoList(1, new AgentInformation[] { pojo1, pojo2 }); - - WebPreparedStatement<?> stmt = new WebPreparedStatement<>(); - stmt.setParams(params); - stmt.setStatementId(WebPreparedStatementResponse.DESCRIPTOR_PARSE_FAILED); - - String jsonString = gson.toJson(stmt, WebPreparedStatement.class); - assertNotNull(jsonString); - - WebPreparedStatement<?> result = gson.fromJson(jsonString, WebPreparedStatement.class); - assertEquals(WebPreparedStatementResponse.DESCRIPTOR_PARSE_FAILED, result.getStatementId()); - assertNotNull(result.getParams()); - - } -} -
--- a/web/common/src/test/java/com/redhat/thermostat/web/common/WebQueryResponseSerializerTest.java Tue Sep 30 11:57:25 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,187 +0,0 @@ -/* - * Copyright 2012-2014 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 java.lang.reflect.Type; - -import org.junit.Before; -import org.junit.Test; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.reflect.TypeToken; -import com.redhat.thermostat.storage.model.AgentInformation; -import com.redhat.thermostat.storage.model.HostInfo; -import com.redhat.thermostat.storage.model.Pojo; - -public class WebQueryResponseSerializerTest { - - private Gson gson; - - @SuppressWarnings("rawtypes") - @Before - public void setup() { - gson = new GsonBuilder() - .registerTypeAdapter(Pojo.class, new ThermostatGSONConverter()) - .registerTypeAdapter(WebQueryResponse.class, - new WebQueryResponseSerializer()).create(); - } - - @Test - public void canSerializeBasic() { - // Our test pojo - AgentInformation agentInfo = new AgentInformation("testing"); - agentInfo.setAlive(false); - AgentInformation[] resultList = new AgentInformation[] { - agentInfo - }; - - // create the query response - WebQueryResponse<AgentInformation> response = new WebQueryResponse<>(); - response.setResultList(resultList); - response.setResponseCode(PreparedStatementResponseCode.ILLEGAL_PATCH); - - String jsonStr = gson.toJson(response); - String expectedJson = "{\"payload\":[{\"startTime\":0,\"stopTime\":0,\"alive\":false,\"agentId\":\"testing\"}],\"errno\":-1}"; - assertEquals(expectedJson, jsonStr); - } - - @Test - public void canDeserializeBasic() { - String rawJson = "{\"payload\":[{\"startTime\":0,\"stopTime\":0,\"alive\":true,\"agentId\":\"testing\"}],\"errno\":-1}"; - Type queryResponseType = new TypeToken<WebQueryResponse<AgentInformation>>() {}.getType(); - WebQueryResponse<AgentInformation> actual = gson.fromJson(rawJson, queryResponseType); - - AgentInformation[] actualList = actual.getResultList(); - - assertEquals(PreparedStatementResponseCode.ILLEGAL_PATCH, actual.getResponseCode()); - assertEquals(1, actualList.length); - AgentInformation actualInfo = actualList[0]; - assertEquals(true, actualInfo.isAlive()); - assertEquals("testing", actualInfo.getAgentId()); - } - - @Test - public void canSerializeAndDeserializeBasic() { - // Our test pojo - AgentInformation agentInfo = new AgentInformation("testing"); - agentInfo.setAlive(false); - AgentInformation[] resultList = new AgentInformation[] { - agentInfo - }; - - // create the query response - WebQueryResponse<AgentInformation> response = new WebQueryResponse<>(); - response.setResultList(resultList); - response.setResponseCode(PreparedStatementResponseCode.ILLEGAL_PATCH); - - String jsonStr = gson.toJson(response); - - // We need to tell GSON which parametrized type we want it to deserialize - // it to. - Type queryResponseType = new TypeToken<WebQueryResponse<AgentInformation>>() {}.getType(); - WebQueryResponse<AgentInformation> actual = gson.fromJson(jsonStr, queryResponseType); - - AgentInformation[] actualList = actual.getResultList(); - - assertEquals(PreparedStatementResponseCode.ILLEGAL_PATCH, actual.getResponseCode()); - assertEquals(1, actualList.length); - AgentInformation actualInfo = actualList[0]; - assertEquals(false, actualInfo.isAlive()); - assertEquals("testing", actualInfo.getAgentId()); - } - - @Test - public void canSerializeAndDeserializeVariousPojos() { - // Our test pojo - AgentInformation agentInfo = new AgentInformation("testing"); - agentInfo.setAlive(false); - AgentInformation[] resultList = new AgentInformation[] { - agentInfo - }; - - // create the query response - WebQueryResponse<AgentInformation> response = new WebQueryResponse<>(); - response.setResultList(resultList); - response.setResponseCode(PreparedStatementResponseCode.ILLEGAL_PATCH); - - String jsonStr = gson.toJson(response); - String expectedJson = "{\"payload\":[{\"startTime\":0,\"stopTime\":0,\"alive\":false,\"agentId\":\"testing\"}],\"errno\":-1}"; - assertEquals(expectedJson, jsonStr); - - // We need to tell GSON which parametrized type we want it to deserialize - // it to. - Type queryResponseType = new TypeToken<WebQueryResponse<AgentInformation>>() {}.getType(); - WebQueryResponse<AgentInformation> actual = gson.fromJson(jsonStr, queryResponseType); - - AgentInformation[] actualList = actual.getResultList(); - - assertEquals(PreparedStatementResponseCode.ILLEGAL_PATCH, actual.getResponseCode()); - assertEquals(1, actualList.length); - AgentInformation actualInfo = actualList[0]; - assertEquals(false, actualInfo.isAlive()); - assertEquals("testing", actualInfo.getAgentId()); - - // Do it again using HostInfo as model - HostInfo hostInfo = new HostInfo(); - hostInfo.setAgentId("something"); - hostInfo.setCpuCount(56); - hostInfo.setHostname("flukebox"); - - HostInfo[] hostInfoResults = new HostInfo[] { - hostInfo - }; - - WebQueryResponse<HostInfo> expected = new WebQueryResponse<>(); - expected.setResultList(hostInfoResults); - expected.setResponseCode(PreparedStatementResponseCode.QUERY_SUCCESS); - - jsonStr = gson.toJson(expected); - Type hostinfoQueryResponseType = new TypeToken<WebQueryResponse<HostInfo>>() {}.getType(); - WebQueryResponse<HostInfo> actualResp = gson.fromJson(jsonStr, hostinfoQueryResponseType); - - assertEquals(PreparedStatementResponseCode.QUERY_SUCCESS, actualResp.getResponseCode()); - HostInfo[] hostInfoList = actualResp.getResultList(); - assertEquals(1, hostInfoList.length); - assertEquals("something", hostInfoList[0].getAgentId()); - assertEquals(56, hostInfoList[0].getCpuCount()); - assertEquals("flukebox", hostInfoList[0].getHostname()); - } -} -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/test/java/com/redhat/thermostat/web/common/typeadapters/JsonPerformanceTest.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,346 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import static org.junit.Assert.assertTrue; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +/** + * + * A generic GSON-based performance test for JSON serialization/deserialization. + * + * @param <T> The type which should get serialized/deserialized. + * + * @see PreparedParameterJSONPerformanceTest + */ +public abstract class JsonPerformanceTest<T> { + + public enum OperationType { + DESERIALIZATION, + SERIALIZATION + } + + public enum GsonContext { + SLOW, + FASTER + } + + private static final String EXPECTATION_FORMAT = " (>=%s)"; + private static final int ITERATIONS_FOR_CUMMULATIVE_AVG = 10; + + private final boolean debug; + private final String testClass; + + protected JsonPerformanceTest(boolean debug, String testClass) { + this.debug = debug; + this.testClass = testClass; + } + + /** + * + * @return The base Gson to measure performance against + */ + protected abstract Gson getSlowGson(); + + /** + * + * @return The "improved" Gson for which the improvement over the Gson as returned by {{@link #getSlowGson()} + * should be measured. + */ + protected abstract Gson getFasterGson(); + + /** + * The expected speedup used in asserts for the given iterations. + * + * @param type The type of operation: serialization or deserialization. + * @param iterations + * @return The expected speed-up. + */ + protected abstract double getExpectedSpeedup(OperationType type, int iterations); + + /** + * Formats the string to a valid json string by using + * {@code mutator}. + * + * @param context The Gson context for this mutation. + * @param mutator A unique number for a performance run. + * + * @return A valid JSON string representing an instance of type T. + */ + protected abstract String mutateJsonString(GsonContext context, int mutator); + + /** + * Creates a mutated instance of T using mutator + * {@code mutator}. + * + * @param mutator A unique number for this performance test run. + * + * @return The mutated instance. + */ + protected abstract T mutateToBeSerializedInstance(int mutator); + + /** + * + * @return The {@link TypeToken} for type T so that it can be properly + * deserialized. + */ + protected abstract TypeToken<T> getDeserializeTypeToken(); + + /** + * + * @return The number of iterations to run for the cold (not warmed-up) + * serialization performance test. + */ + protected abstract int getColdSerializationIterations(); + + /** + * + * @return The number of iterations to run for the "warmed-up" serialization + * performance test. + */ + protected abstract int getWarmSerializationIterations(); + + /** + * + * @return The number of iterations to run for the cold (not warmed-up) + * deserialization performance test. + */ + protected abstract int getColdDeserializationIterations(); + + /** + * + * @return The number of iterations to run for the "warmed-up" deserialization + * performance test. + */ + protected abstract int getWarmDeserializationIterations(); + + /** + * + * @return A delta to increase the "faster" gson serialization speed-up so + * that basic assertions such as {@code speed-up >= 1} work. This is + * useful for the base-case of comparing a gson instance to itself. + * Two performance runs of the same gson are expected to be + * approximately equally fast. Though, it will depend on the system. + */ + protected abstract double getSelfSerializationDelta(); + + /** + * + * @return A delta to increase the "faster" gson deserialization speed-up so + * that basic assertions such as {@code speed-up >= 1} work. This is + * useful for the base-case of comparing a gson instance to itself. + * Two performance runs of the same gson are expected to be + * approximately equally fast. Though, it will depend on the system. + */ + protected abstract double getSelfDeserializationDelta(); + + @Test + public void verifySerializationSpeedCold() { + int iterations = getColdSerializationIterations(); + double actualSpeedup = getAverageSerializationSpeedup(iterations); + final double expectedSpeedup = getExpectedSpeedup(OperationType.SERIALIZATION, iterations); + if (debug) { + System.out.println(testClass + ": actual cold serialization speed-up: " + + formatSpeedup(actualSpeedup) + String.format(EXPECTATION_FORMAT, formatSpeedup(expectedSpeedup))); + } + assertTrue("Performance regression? Expected a speed-up of > " + expectedSpeedup +". Speed-up was: " + actualSpeedup, actualSpeedup > expectedSpeedup); + } + + @Test + public void verifySerializationSpeedWarm() { + int iterations = getWarmSerializationIterations(); + double actualSpeedup = getAverageSerializationSpeedup(iterations); + final double expectedSpeedup = getExpectedSpeedup(OperationType.SERIALIZATION, iterations); + if (debug) { + System.out.println(testClass + ": actual warmed-up serialization speed-up: " + + formatSpeedup(actualSpeedup) + String.format(EXPECTATION_FORMAT, formatSpeedup(expectedSpeedup))); + } + assertTrue("Performance regression? Expected a speed-up of > " + expectedSpeedup +". Speed-up was: " + actualSpeedup, actualSpeedup > expectedSpeedup); + } + + @Test + public void verifyDeserializationSpeedCold() { + int iterations = getColdDeserializationIterations(); + double actualSpeedup = getAverageDeserializationSpeedup(iterations); + final double expectedSpeedup = getExpectedSpeedup(OperationType.DESERIALIZATION, iterations); + if (debug) { + System.out.println(testClass + ": actual cold deserialization speed-up: " + + formatSpeedup(actualSpeedup) + String.format(EXPECTATION_FORMAT, formatSpeedup(expectedSpeedup))); + } + assertTrue("Performance regression? Expected a speed-up of > " + expectedSpeedup +". Speed-up was: " + actualSpeedup, actualSpeedup > expectedSpeedup); + } + + @Test + public void verifyDeserializationSpeedWarm() { + int iterations = getWarmDeserializationIterations(); + double actualSpeedup = getAverageDeserializationSpeedup(iterations); + final double expectedSpeedup = getExpectedSpeedup(OperationType.DESERIALIZATION, iterations); + if (debug) { + System.out.println(testClass + ": actual warmed-up deserialization speed-up: " + + formatSpeedup(actualSpeedup) + String.format(EXPECTATION_FORMAT, formatSpeedup(expectedSpeedup))); + } + assertTrue("Performance regression? Expected a speed-up of > " + expectedSpeedup +". Speed-up was: " + actualSpeedup, actualSpeedup > expectedSpeedup); + } + + private double getAverageSerializationSpeedup(final int iterations) { + double sum = 0; + for (int i = 0; i < ITERATIONS_FOR_CUMMULATIVE_AVG; i++) { + sum += measureSerializationSpeed(iterations); + } + double speedup = sum/ITERATIONS_FOR_CUMMULATIVE_AVG; + double delta = getSelfSerializationDelta(); + assertTrue("Performance Regression? Expected a speed-up of >= 1, but was: " + (speedup + delta), + (speedup + delta) >= 1); + return speedup; + } + + private double measureSerializationSpeed(final int iterations) { + final Gson oldGson = getSlowGson(); + final Gson newGson = getFasterGson(); + PerfTestResult result = runSerializationPerformanceTest(iterations, oldGson, newGson); + return result.getSpeedup(); + } + + private double getAverageDeserializationSpeedup(final int iterations) { + double sum = 0; + for (int i = 0; i < ITERATIONS_FOR_CUMMULATIVE_AVG; i++) { + sum += measureDeserializationSpeed(iterations); + } + double speedup = sum/ITERATIONS_FOR_CUMMULATIVE_AVG; + double delta = getSelfDeserializationDelta(); + assertTrue("Performance Regression? Expected a speed-up of >= 1, but was: " + (speedup + delta), + (speedup + delta) >= 1); + return speedup; + } + + private double measureDeserializationSpeed(final int iterations) { + final Gson oldGson = getSlowGson(); + final Gson newGson = getFasterGson(); + PerfTestResult result = runDeserializationPerformanceTest(iterations, oldGson, newGson); + return result.getSpeedup(); + } + + private PerfTestResult runSerializationPerformanceTest(final int iterations, final Gson oldGson, final Gson newGson) { + List<String> list = new ArrayList<>(); + double oldSum = 0; + double newSum = 0; + long start = -1; + long end = -1; + Gson gson = null; + for (int i = 0; i < iterations * 2; i++) { + T instance = mutateToBeSerializedInstance(i); + if (i % 2 != 0) { + gson = newGson; + } else { + gson = oldGson; + } + start = System.nanoTime(); + String json = gson.toJson(instance); + end = System.nanoTime(); + if (i % 2 != 0) { + newSum += (end - start); + } else { + oldSum += (end - start); + } + // Do something silly just so that the JIT does not optimize-out the + // toJson() call. + list.add(json); + } + PerfTestResult res = new PerfTestResult(); + res.oldPerf = oldSum/iterations; + res.newPerf = newSum/iterations; + return res; + } + + private PerfTestResult runDeserializationPerformanceTest(final int iterations, final Gson oldGson, final Gson newGson) { + List<T> list = new ArrayList<>(); + + double oldSum = 0; + double newSum = 0; + long start = -1; + long end = -1; + Gson gson = null; + String json; + for (int i = 0; i < iterations * 2; i++) { + if (i % 2 != 0) { + json = mutateJsonString(GsonContext.FASTER, i); + gson = newGson; + } else { + json = mutateJsonString(GsonContext.SLOW, i); + gson = oldGson; + } + start = System.nanoTime(); + T instance = gson.fromJson(json, getDeserializeTypeToken().getType()); + end = System.nanoTime(); + if (i % 2 != 0) { + newSum += (end - start); + } else { + oldSum += (end - start); + } + // Do something silly just so that the JIT does not optimize-out the + // fromJson() call. + list.add(instance); + } + PerfTestResult res = new PerfTestResult(); + res.oldPerf = oldSum/iterations; + res.newPerf = newSum/iterations; + return res; + } + + private String formatSpeedup(double value) { + DecimalFormat format = new DecimalFormat("#.##"); + return format.format(value); + } + + private static class PerfTestResult { + private double oldPerf; + private double newPerf; + + private double getSpeedup() { + return oldPerf/newPerf; + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/test/java/com/redhat/thermostat/web/common/typeadapters/LegacyGSONConverter.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,114 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Type; + +import org.apache.commons.beanutils.PropertyUtils; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.redhat.thermostat.storage.core.Entity; +import com.redhat.thermostat.storage.core.Persist; +import com.redhat.thermostat.storage.model.Pojo; + +/** + * Old non-stream GSON API serializer. Used only in performance tests. + */ +public class LegacyGSONConverter implements JsonSerializer<Pojo>, JsonDeserializer<Pojo> { + + @Override + public Pojo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + @SuppressWarnings("unchecked") + Class<? extends Pojo> targetType = (Class<Pojo>) typeOfT; + try { + Pojo pojo = targetType.newInstance(); + PropertyDescriptor[] descs = PropertyUtils.getPropertyDescriptors(pojo); + for (PropertyDescriptor desc : descs) { + Method writeMethod = desc.getWriteMethod(); + if (writeMethod != null && writeMethod.isAnnotationPresent(Persist.class)) { + String name = desc.getName(); + JsonElement child = json.getAsJsonObject().get(name); + Object value = context.deserialize(child, desc.getPropertyType()); + PropertyUtils.setProperty(pojo, name, value); + } + } + return pojo; + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + @Override + public JsonElement serialize(Pojo src, Type typeOfSrc, JsonSerializationContext context) { + Class<?> cls = (Class<?>) typeOfSrc; + if (! cls.isAnnotationPresent(Entity.class)) { + System.err.println("attempt to serialize non-Entity class: " + cls.getName()); + throw new IllegalArgumentException("attempt to serialize non-Entity class: " + cls.getName()); + } + JsonObject obj = new JsonObject(); + PropertyDescriptor[] descs = PropertyUtils.getPropertyDescriptors(src); + for (PropertyDescriptor desc : descs) { + Method readMethod = desc.getReadMethod(); + if (readMethod != null && readMethod.isAnnotationPresent(Persist.class)) { + String name = desc.getName(); + + try { + Object value = PropertyUtils.getProperty(src, name); + obj.add(name, context.serialize(value)); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + } else if (readMethod == null) { + System.err.println("WARNING: property without read method: " + src.getClass().getName() + "::" + desc.getName()); + } + } + return obj; + } + + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/test/java/com/redhat/thermostat/web/common/typeadapters/LegacyPreparedParameterSerializer.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,192 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import java.lang.reflect.Array; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.redhat.thermostat.common.utils.LoggingUtils; +import com.redhat.thermostat.storage.core.PreparedParameter; +import com.redhat.thermostat.storage.model.Pojo; + +/** + * Old non-stream GSON API serializer. Used only in performance tests. + */ +public class LegacyPreparedParameterSerializer implements JsonSerializer<PreparedParameter>, + JsonDeserializer<PreparedParameter>{ + + private static final Logger logger = LoggingUtils.getLogger(LegacyPreparedParameterSerializer.class); + private static final String PROP_TYPE = "type"; + private static final String PROP_VALUE = "value"; + 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 { + 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()); + result.add(PROP_VALUE, valueElem); + JsonPrimitive typeElem = new JsonPrimitive(param.getType().getName()); + result.add(PROP_TYPE, typeElem); + return result; + } + + private JsonElement serializeValue(JsonSerializationContext ctxt, Object value, Class<?> type) { + JsonElement element; + // 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, type); + } + return element; + } + + @Override + public PreparedParameter deserialize(JsonElement jsonElem, Type type, + JsonDeserializationContext ctxt) throws JsonParseException { + JsonElement typeElem = jsonElem.getAsJsonObject().get(PROP_TYPE); + String className = typeElem.getAsString(); + // perform some sanity checking on the types of classes we actually + // de-serialize + Class<?> typeVal = deserializeTypeVal(className); + validateSaneClassName(typeVal); + JsonElement valueElement = jsonElem.getAsJsonObject().get(PROP_VALUE); + Object value = deserializeValue(ctxt, valueElement, typeVal); + PreparedParameter param = new PreparedParameter(); + param.setType(typeVal); + param.setValue(value); + return param; + } + + private Class<?> deserializeTypeVal(String className) { + Class<?> typeVal = null; + 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) { + // 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(); + return ctxt.deserialize(valueElement, arrayType); + } else { + Object value = ctxt.deserialize(valueElement, valType); + validatePrimitivesForNull(value, valType); + return value; + } + } + + 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) { + // isAssignableFrom throws NPE if clazz is null. + if (VALID_CLASSES.contains(clazz) || + Pojo.class.isAssignableFrom(clazz)) { + return; + } + throw new IllegalStateException("Illegal type of parameter " + clazz.getCanonicalName()); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/test/java/com/redhat/thermostat/web/common/typeadapters/LegacyWebPreparedStatementSerializer.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,88 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import java.lang.reflect.Type; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.redhat.thermostat.storage.core.PreparedParameters; +import com.redhat.thermostat.web.common.WebPreparedStatement; + +/** + * Old non-stream GSON API serializer. Used only in performance tests. + * + */ +public class LegacyWebPreparedStatementSerializer implements + JsonDeserializer<WebPreparedStatement<?>>, + JsonSerializer<WebPreparedStatement<?>> { + + private static final String PROP_PARAMS = "p"; + private static final String PROP_STMT_ID = "sid"; + + @Override + public JsonElement serialize(WebPreparedStatement<?> stmt, Type type, + JsonSerializationContext ctxt) { + JsonObject result = new JsonObject(); + JsonElement parameters = ctxt.serialize(stmt.getParams(), PreparedParameters.class); + result.add(PROP_PARAMS, parameters); + JsonPrimitive stmtIdElem = new JsonPrimitive(stmt.getStatementId()); + result.add(PROP_STMT_ID, stmtIdElem); + return result; + } + + @Override + public WebPreparedStatement<?> deserialize(JsonElement jsonElem, Type type, + JsonDeserializationContext ctxt) throws JsonParseException { + JsonElement paramsElem = jsonElem.getAsJsonObject().get(PROP_PARAMS); + JsonElement stmtIdElem = jsonElem.getAsJsonObject().get(PROP_STMT_ID); + PreparedParameters params = ctxt.deserialize(paramsElem, PreparedParameters.class); + int stmtId = ctxt.deserialize(stmtIdElem, int.class); + WebPreparedStatement<?> stmt = new WebPreparedStatement<>(); + stmt.setStatementId(stmtId); + stmt.setParams(params); + return stmt; + } + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/test/java/com/redhat/thermostat/web/common/typeadapters/LegacyWebQueryResponseSerializer.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,101 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import java.lang.reflect.Array; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.redhat.thermostat.storage.model.Pojo; +import com.redhat.thermostat.web.common.WebQueryResponse; + +/** + * Old non-stream GSON API serializer. Used only in performance tests. + */ +public class LegacyWebQueryResponseSerializer<T extends Pojo> implements + JsonDeserializer<WebQueryResponse<T>>, JsonSerializer<WebQueryResponse<T>> { + + private static final String PROP_RESULT = "payload"; + private static final String PROP_ERROR_CODE = "errno"; + + @Override + public JsonElement serialize(WebQueryResponse<T> qResponse, Type type, + JsonSerializationContext ctxt) { + JsonObject result = new JsonObject(); + JsonElement resultsElem = ctxt.serialize(qResponse.getResultList()); + result.add(PROP_RESULT, resultsElem); + JsonPrimitive errnoElem = new JsonPrimitive(qResponse.getResponseCode()); + result.add(PROP_ERROR_CODE, errnoElem); + return result; + } + + @SuppressWarnings("unchecked") + @Override + public WebQueryResponse<T> deserialize(JsonElement jsonElem, Type type, + JsonDeserializationContext ctxt) throws JsonParseException { + // fromJson() calls need to pass in the right *parameterized* type token: + // example for AgentInformation as T: + // Type queryResponseType = new TypeToken<WebQueryResponse<AgentInformation>>() {}.getType(); + // gson.fromJson(jsonStr, queryResponseType) + Type[] typeParameters = ((ParameterizedType)type).getActualTypeArguments(); + Type queryResponseTypeParam = typeParameters[0]; // WebQueryResponse has only one parameterized type T + JsonArray resultElem = jsonElem.getAsJsonObject().get(PROP_RESULT).getAsJsonArray(); + @SuppressWarnings("rawtypes") + Class typeOfGeneric = (Class)queryResponseTypeParam; + T[] array = (T[])Array.newInstance(typeOfGeneric, resultElem.size()); + for (int i = 0; i < resultElem.size(); i++) { + array[i] = ctxt.deserialize(resultElem.get(i), queryResponseTypeParam); + } + JsonElement errorCodeElem = jsonElem.getAsJsonObject().get(PROP_ERROR_CODE); + int errorCode = ctxt.deserialize(errorCodeElem, int.class); + WebQueryResponse<T> qResponse = new WebQueryResponse<>(); + qResponse.setResponseCode(errorCode); + qResponse.setResultList(array); + return qResponse; + } + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/test/java/com/redhat/thermostat/web/common/typeadapters/PojoTypeAdapterTest.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,319 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +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 org.junit.Before; +import org.junit.Test; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.redhat.thermostat.storage.core.Entity; +import com.redhat.thermostat.storage.core.Persist; +import com.redhat.thermostat.storage.model.AgentInformation; +import com.redhat.thermostat.storage.model.AggregateCount; +import com.redhat.thermostat.storage.model.Pojo; + +public class PojoTypeAdapterTest { + + private Gson gson; + + @Before + public void setup() { + gson = new GsonBuilder() + .registerTypeAdapterFactory(new PojoTypeAdapterFactory()) + .create(); + } + + @Test + public void canSerializePojoLists() { + String expectedJson = "[{\"AInt\":200,\"fooString\":\"bar\"}," + + "{\"AInt\":5,\"fooString\":\"baz\"}," + + "{\"AInt\":200,\"fooString\":\"bar\"}]"; + + FooPojo pojo1 = new FooPojo(); + pojo1.setAInt(200); + pojo1.setFooString("bar"); + + FooPojo pojo2 = new FooPojo(); + pojo2.setAInt(5); + pojo2.setFooString("baz"); + + FooPojo[] list = new FooPojo[] { + pojo1, pojo2, pojo1 + }; + String json = gson.toJson(list); + assertEquals(expectedJson, json); + } + + @Test + public void canDeserializePojoLists() { + String json = "[{\"AInt\":200,\"fooString\":\"bar\"}," + + "{\"AInt\":5,\"fooString\":\"baz\"}," + + "{\"AInt\":200,\"fooString\":\"bar\"}]"; + + FooPojo[] actual = gson.fromJson(json, FooPojo[].class); + assertEquals(3, actual.length); + assertEquals(200, actual[0].getAInt()); + assertEquals(5, actual[1].getAInt()); + assertEquals(200, actual[2].getAInt()); + assertEquals("bar", actual[0].getFooString()); + assertEquals("bar", actual[2].getFooString()); + assertEquals("baz", actual[1].getFooString()); + } + + @Test + public void canSerializeNullPojo() { + FooPojo foo = null; + String jsonStr = gson.toJson(foo); + assertEquals("null", jsonStr); + } + + // Null values won't show up in the JSON string. + @Test + public void canSerializeNullMember() { + String expectedJson = "{\"AInt\":200,\"fooString\":\"bar\"}"; + + FooPojo foo = new FooPojo(); + foo.setAInt(200); + foo.setFooString("bar"); + String jsonStr = gson.toJson(foo); + assertEquals(expectedJson, jsonStr); + } + + @Test + public void canDeserializeWithMembersUnset() { + String json = "{\"AInt\":200,\"boolArray\":null,\"fooString\":\"bar\"}"; + + FooPojo pojo = gson.fromJson(json, FooPojo.class); + assertNotNull(pojo); + assertEquals(200, pojo.getAInt()); + assertEquals("bar", pojo.getFooString()); + assertNull(pojo.getBoolArray()); + + // Do it again with unset values omitted. + json = "{\"fooString\":\"bar\"}"; + pojo = gson.fromJson(json, FooPojo.class); + assertNotNull(pojo); + assertEquals("bar", pojo.getFooString()); + assertNull(pojo.getBoolArray()); + assertEquals("0 is default int val", 0, pojo.getAInt()); + } + + @Test + public void canDeserializeBasic() { + String json = "{\"AInt\":1223,\"boolArray\":[false,true,false],\"fooString\":\"fooStringValue\"}"; + + FooPojo pojo = gson.fromJson(json, FooPojo.class); + assertEquals(1223, pojo.getAInt()); + assertEquals("fooStringValue", pojo.getFooString()); + assertEquals(3, pojo.getBoolArray().length); + assertFalse(pojo.getBoolArray()[0]); + assertTrue(pojo.getBoolArray()[1]); + assertFalse(pojo.getBoolArray()[2]); + } + + @Test + public void canDeserializeNullPojo() { + String nullStr = "null"; + FooPojo pojo = gson.fromJson(nullStr, FooPojo.class); + assertNull(pojo); + } + + @Test + public void canSerializeBasic() { + String expectedJson = "{\"AInt\":1223,\"boolArray\":[false,true,false],\"fooString\":\"fooStringValue\"}"; + + FooPojo fooPojo = new FooPojo(); + fooPojo.setBoolArray(new boolean[] { false, true, false }); + fooPojo.setAInt(1223); + fooPojo.setFooString("fooStringValue"); + String jsonStr = gson.toJson(fooPojo); + assertEquals(expectedJson, jsonStr); + } + + @Test + public void canSerializeRecursivePojo() { + FooPojo fooPojo = new FooPojo(); + fooPojo.setBoolArray(new boolean[] { false, true, false }); + fooPojo.setAInt(1223); + fooPojo.setFooString("fooStringValue"); + String fooPojoJson = gson.toJson(fooPojo); + + String expectedJson = "{\"myLong\":10000000000,\"pojo\":" + fooPojoJson + "}"; + RecPojo recursive = new RecPojo(); + recursive.setPojo(fooPojo); + recursive.setMyLong(10000000000L); + + String jsonStr = gson.toJson(recursive); + assertEquals(expectedJson, jsonStr); + } + + @Test + public void canDeserializeRecursivePojo() { + String json = "{\"myLong\":10000000000,\"pojo\":" + + "{\"AInt\":1223,\"boolArray\":[false,true,false],\"fooString\":\"fooStringValue\"}}"; + + RecPojo rec = gson.fromJson(json, RecPojo.class); + + assertEquals(10000000000L, rec.getMyLong()); + assertNotNull(rec.getPojo()); + + FooPojo pojo = rec.getPojo(); + assertEquals(1223, pojo.getAInt()); + assertEquals("fooStringValue", pojo.getFooString()); + assertEquals(3, pojo.getBoolArray().length); + assertFalse(pojo.getBoolArray()[0]); + assertTrue(pojo.getBoolArray()[1]); + assertFalse(pojo.getBoolArray()[2]); + } + + @Test + public void canSerializeDeserializeBasic() { + // Our test pojo + AgentInformation agentInfo = new AgentInformation("testing"); + agentInfo.setAlive(true); + + String jsonStr = gson.toJson(agentInfo); + + AgentInformation actual = gson.fromJson(jsonStr, AgentInformation.class); + + assertEquals("testing", actual.getAgentId()); + assertEquals(true, actual.isAlive()); + } + + @Test + public void canSerializeDeserializeArray() { + // Our test pojo + AgentInformation agentInfo = new AgentInformation("testing"); + agentInfo.setAlive(true); + AgentInformation[] agentInfos = new AgentInformation[] { + agentInfo + }; + + String jsonStr = gson.toJson(agentInfos); + + AgentInformation[] actual = gson.fromJson(jsonStr, AgentInformation[].class); + + assertEquals("testing", actual[0].getAgentId()); + assertEquals(true, actual[0].isAlive()); + } + + @Test + public void canSerializeDeserializeAggregateCount() { + long expectedCount = 3333000333L; + AggregateCount count = new AggregateCount(); + count.setCount(expectedCount); + String jsonStr = gson.toJson(count); + // now do the reverse + AggregateCount c2 = gson.fromJson(jsonStr, AggregateCount.class); + assertEquals(expectedCount, c2.getCount()); + } + + @Entity + public static class FooPojo implements Pojo { + + private String fooString; + private int fooInt; + private boolean[] boolArray; + + @Persist + public String getFooString() { + return fooString; + } + + @Persist + public void setFooString(String fooString) { + this.fooString = fooString; + } + + @Persist + public int getAInt() { + return fooInt; + } + + @Persist + public void setAInt(int fooInt) { + this.fooInt = fooInt; + } + + @Persist + public boolean[] getBoolArray() { + return boolArray; + } + + @Persist + public void setBoolArray(boolean[] boolArray) { + this.boolArray = boolArray; + } + + + } + + @Entity + public static class RecPojo implements Pojo { + + private long myLong; + private FooPojo pojo; + + @Persist + public long getMyLong() { + return myLong; + } + + @Persist + public void setMyLong(long myLong) { + this.myLong = myLong; + } + + @Persist + public FooPojo getPojo() { + return pojo; + } + + @Persist + public void setPojo(FooPojo pojo) { + this.pojo = pojo; + } + + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/test/java/com/redhat/thermostat/web/common/typeadapters/PreparedParameterJSONPerformanceTest.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,146 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import org.junit.experimental.categories.Category; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import com.redhat.thermostat.storage.core.PreparedParameter; +import com.redhat.thermostat.testutils.PerformanceTest; +import com.redhat.thermostat.web.common.typeadapters.PojoTypeAdapterFactory; +import com.redhat.thermostat.web.common.typeadapters.PreparedParameterTypeAdapterFactory; + +/** + * JUnit categorized performance test. It'll be only run for + * the perf-tests profile during a full build. + */ +@Category(PerformanceTest.class) +public class PreparedParameterJSONPerformanceTest extends + JsonPerformanceTest<PreparedParameter> { + + private static final boolean DEBUG = true; + + public PreparedParameterJSONPerformanceTest() { + super(DEBUG, PreparedParameterJSONPerformanceTest.class.getSimpleName()); + } + + @Override + protected Gson getSlowGson() { + return new GsonBuilder() + .registerTypeAdapterFactory(new PojoTypeAdapterFactory()) + .registerTypeAdapter(PreparedParameter.class, new LegacyPreparedParameterSerializer()) + .create(); + } + + @Override + protected Gson getFasterGson() { + return new GsonBuilder() + .registerTypeAdapterFactory(new PojoTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParameterTypeAdapterFactory()) + .create(); + } + + @Override + protected double getExpectedSpeedup( + OperationType type, + int iterations) { + if (type == OperationType.SERIALIZATION && iterations == getColdSerializationIterations()) { + return 1.7; + } else if (type == OperationType.SERIALIZATION && iterations == getWarmSerializationIterations()) { + return 1.3; + } else if (type == OperationType.DESERIALIZATION && iterations == getColdDeserializationIterations()) { + return 1; + } else if (type == OperationType.DESERIALIZATION && iterations == getWarmDeserializationIterations()) { + return 1; + } else { + throw new IllegalStateException("bug"); + } + } + + @Override + protected String mutateJsonString( + GsonContext context, + int mutator) { + return String.format("{\"type\":\"int\",\"value\":%d}", mutator); + } + + @Override + protected PreparedParameter mutateToBeSerializedInstance(int mutator) { + PreparedParameter param = new PreparedParameter(); + param.setValue(mutator); + param.setType(int.class); + return param; + } + + @Override + protected TypeToken<PreparedParameter> getDeserializeTypeToken() { + return TypeToken.get(PreparedParameter.class); + } + + @Override + protected int getColdSerializationIterations() { + return 1; + } + + @Override + protected int getWarmSerializationIterations() { + return 1000; + } + + @Override + protected int getColdDeserializationIterations() { + return 1; + } + + @Override + protected int getWarmDeserializationIterations() { + return 1000; + } + + @Override + protected double getSelfSerializationDelta() { + return 0.1; + } + + @Override + protected double getSelfDeserializationDelta() { + return 0.1; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/test/java/com/redhat/thermostat/web/common/typeadapters/PreparedParameterTypeAdapterTest.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,481 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.lang.reflect.Array; + +import org.junit.Before; +import org.junit.Test; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.redhat.thermostat.storage.core.PreparedParameter; +import com.redhat.thermostat.storage.model.AgentInformation; +import com.redhat.thermostat.storage.model.VmInfo.KeyValuePair; + +public class PreparedParameterTypeAdapterTest { + + private Gson gson; + + @Before + public void setup() { + gson = new GsonBuilder() + .registerTypeAdapterFactory(new PojoTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParameterTypeAdapterFactory()) + .create(); + } + + @Test + public void canDeserializeBasic() { + // String + String jsonStr = "{ \"type\": \"java.lang.String\" , \"value\": \"testing\" }"; + PreparedParameter param = gson.fromJson(jsonStr, PreparedParameter.class); + assertEquals(String.class, param.getType()); + assertEquals("testing", param.getValue()); + // Integer + jsonStr = "{ \"type\": \"int\" , \"value\": -1}"; + param = gson.fromJson(jsonStr, PreparedParameter.class); + assertEquals(int.class, param.getType()); + assertEquals(-1, param.getValue()); + // Long + jsonStr = "{ \"type\": \"long\" , \"value\": -10}"; + param = gson.fromJson(jsonStr, PreparedParameter.class); + assertEquals(long.class, param.getType()); + assertEquals(-10L, param.getValue()); + jsonStr = "{ \"type\": \"long\" , \"value\": 30000000003}"; + param = gson.fromJson(jsonStr, PreparedParameter.class); + assertEquals(long.class, param.getType()); + assertEquals(30000000003L, param.getValue()); + // Boolean + jsonStr = "{ \"type\": \"boolean\" , \"value\": true}"; + param = gson.fromJson(jsonStr, PreparedParameter.class); + assertEquals(boolean.class, param.getType()); + assertEquals(true, param.getValue()); + // String[] + String strArrayVal = "[ \"testing1\", \"testing2\", \"3\" ]"; + jsonStr = "{ \"type\": \"[Ljava.lang.String;\" , \"value\": " + strArrayVal + "}"; + param = gson.fromJson(jsonStr, PreparedParameter.class); + assertEquals(String[].class, param.getType()); + assertTrue(param.getValue() instanceof String[]); + String[] vals = (String[])param.getValue(); + assertEquals(3, vals.length); + assertEquals("testing1", vals[0]); + assertEquals("testing2", vals[1]); + assertEquals("3", vals[2]); + } + + @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 { + gson.fromJson(jsonStr, PreparedParameter.class); + fail("should have failed to serialize"); + } catch (Exception e) { + // pass + Throwable cause = e.getCause(); + assertTrue(cause.getMessage().contains("Illegal type of parameter")); + } + } + + @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 = "{\"type\":\"java.lang.String\",\"value\":\"testing\"}"; + PreparedParameter param = new PreparedParameter(); + param.setType(String.class); + param.setValue("testing"); + String actual = gson.toJson(param); + assertEquals(expected, actual); + // Integer + expected = "{\"type\":\"int\",\"value\":-1}"; + param.setType(int.class); + param.setValue(-1); + actual = gson.toJson(param); + assertEquals(expected, actual); + // Long + expected = "{\"type\":\"long\",\"value\":30000000003}"; + param.setType(long.class); + param.setValue(30000000003L); + actual = gson.toJson(param); + assertEquals(expected, actual); + // boolean + expected = "{\"type\":\"boolean\",\"value\":true}"; + param.setType(boolean.class); + param.setValue(true); + actual = gson.toJson(param); + assertEquals(expected, actual); + // String[] + String strArrayVal = "[\"testing1\",\"testing2\",\"3\"]"; + expected = "{\"type\":\"[Ljava.lang.String;\",\"value\":" + strArrayVal + "}"; + param.setType(String[].class); + String[] array = new String[] { + "testing1", "testing2", "3" + }; + param.setValue(array); + actual = gson.toJson(param); + assertEquals(expected, actual); + } + + @Test + public void canSerializeDeserializeInteger() { + PreparedParameter expected = new PreparedParameter(); + expected.setType(int.class); + expected.setValue(3); + String jsonStr = gson.toJson(expected, PreparedParameter.class); + assertParameterEquals(expected, jsonStr); + } + + @Test + public void canSerializeDeserializeIntegerArray() { + PreparedParameter expected = new PreparedParameter(); + expected.setType(int[].class); + expected.setValue(new int[] { 0, 3, 20 }); + String jsonStr = gson.toJson(expected, PreparedParameter.class); + assertParameterEquals(expected, jsonStr); + } + + @Test + public void canSerializeDeserializeDouble() { + PreparedParameter expected = new PreparedParameter(); + expected.setType(double.class); + expected.setValue(Math.E); + String jsonStr = gson.toJson(expected, PreparedParameter.class); + assertParameterEquals(expected, jsonStr); + } + + @Test + public void canSerializeDeserializeDoubleArray() { + PreparedParameter expected = new PreparedParameter(); + expected.setType(double[].class); + expected.setValue(new double[] { 3.3, 1.0, Math.PI }); + String jsonStr = gson.toJson(expected, PreparedParameter.class); + assertParameterEquals(expected, jsonStr); + } + + @Test + public void canSerializeDeserializeLong() { + PreparedParameter expected = new PreparedParameter(); + expected.setType(long.class); + expected.setValue(30000000003L); + String jsonStr = gson.toJson(expected, PreparedParameter.class); + assertParameterEquals(expected, jsonStr); + } + + @Test + public void canSerializeDeserializeLongArray() { + PreparedParameter expected = new PreparedParameter(); + expected.setType(long[].class); + expected.setValue(new long[] { 3000000000L, 3, 20 }); + String jsonStr = gson.toJson(expected, PreparedParameter.class); + assertParameterEquals(expected, jsonStr); + } + + @Test + public void canSerializeDeserializeString() { + PreparedParameter expected = new PreparedParameter(); + expected.setType(String.class); + expected.setValue("testing"); + String jsonStr = gson.toJson(expected, PreparedParameter.class); + assertParameterEquals(expected, jsonStr); + } + + @Test + public void canSerializeDeserializeStringArray() { + PreparedParameter expected = new PreparedParameter(); + expected.setType(String[].class); + String[] expectedArray = new String[] { + "one", "two", "three" + }; + expected.setValue(expectedArray); + String jsonStr = gson.toJson(expected, PreparedParameter.class); + assertParameterEquals(expected, jsonStr); + } + + @Test + public void canSerializeDeserializeBoolean() { + PreparedParameter expected = new PreparedParameter(); + 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.setValue(true); + jsonStr = gson.toJson(expected, PreparedParameter.class); + assertParameterEquals(expected, jsonStr); + } + + @Test + public void canSerializeDeserializeBooleanArray() { + PreparedParameter expected = new PreparedParameter(); + expected.setType(boolean[].class); + expected.setValue(new boolean[] { true, false, false, true }); + String jsonStr = gson.toJson(expected, PreparedParameter.class); + assertParameterEquals(expected, jsonStr); + } + + + @Test + public void canSerializeDeserializePojos() { + PreparedParameter expected = new PreparedParameter(); + AgentInformation info = new AgentInformation("foo-writer"); + expected.setType(info.getClass()); + expected.setValue(info); + String jsonStr = gson.toJson(expected, PreparedParameter.class); + assertParameterEquals(expected, jsonStr); + + info = new AgentInformation("some-writer"); + info.setAlive(true); + info.setConfigListenAddress("127.0.0.1:12000"); + info.setStartTime(System.currentTimeMillis()); + info.setStopTime(System.currentTimeMillis()); + expected = new PreparedParameter(); + expected.setType(info.getClass()); + expected.setValue(info); + 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); + } + + @Test + public void canSerializeDeserializeInnerClassPojoTypes() { + PreparedParameter expected = new PreparedParameter(); + KeyValuePair pair = new KeyValuePair(); + pair.setKey("foo"); + pair.setValue("bar"); + expected.setType(pair.getClass()); + expected.setValue(pair); + String jsonStr = gson.toJson(expected, PreparedParameter.class); + + PreparedParameter actual = gson.fromJson(jsonStr, PreparedParameter.class); + + assertEquals(expected.getType(), actual.getType()); + assertTrue(actual.getValue() instanceof KeyValuePair); + KeyValuePair actualPair = (KeyValuePair)actual.getValue(); + assertEquals(pair.getKey(), actualPair.getKey()); + assertEquals(pair.getValue(), actualPair.getValue()); + } + + @Test + public void canSerializeDeserializePojoLists() { + AgentInformation info1 = new AgentInformation("foo-writer"); + AgentInformation info2 = new AgentInformation("some-writer"); + info2.setAlive(true); + info2.setConfigListenAddress("127.0.0.1:12000"); + info2.setStartTime(System.currentTimeMillis()); + info2.setStopTime(System.currentTimeMillis()); + AgentInformation[] infos = new AgentInformation[] { + info1, info2 + }; + PreparedParameter param = new PreparedParameter(); + param.setType(AgentInformation.class); + param.setValue(infos); + String jsonStr = gson.toJson(param, PreparedParameter.class); + assertParameterEquals(param, jsonStr); + } + + private void assertParameterEquals(PreparedParameter expected, + String jsonStr) { + PreparedParameter actual = gson.fromJson(jsonStr, PreparedParameter.class); + assertEquals(expected.getType(), actual.getType()); + if (actual.getValue() != null && actual.getValue().getClass().isArray()) { + // compare element by element + Object values = actual.getValue(); + Object expectedVals = expected.getValue(); + int expectedLength = Array.getLength(expectedVals); + int actualLength = Array.getLength(values); + assertEquals(expectedLength, actualLength); + // Make sure the deserialized array is of the correct expected type + assertEquals(expectedVals.getClass(), values.getClass()); + for (int i = 0; i < expectedLength; i++) { + Object exp = Array.get(expectedVals, i); + Object act = Array.get(values, i); + assertEquals(exp, act); + } + } else { + assertEquals(expected.getValue(), actual.getValue()); + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/test/java/com/redhat/thermostat/web/common/typeadapters/PreparedParametersJSONPerformanceTest.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,151 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import org.junit.experimental.categories.Category; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import com.redhat.thermostat.storage.core.PreparedParameters; +import com.redhat.thermostat.testutils.PerformanceTest; +import com.redhat.thermostat.web.common.typeadapters.PojoTypeAdapterFactory; +import com.redhat.thermostat.web.common.typeadapters.PreparedParameterTypeAdapterFactory; +import com.redhat.thermostat.web.common.typeadapters.PreparedParametersTypeAdapterFactory; + +/** + * JUnit categorized performance test. It'll be only run for + * the perf-tests profile. + */ +@Category(PerformanceTest.class) +public class PreparedParametersJSONPerformanceTest extends + JsonPerformanceTest<PreparedParameters> { + + private static final String OLD_JSON_FORMAT = "{\"params\":[]}"; + private static final String NEW_JSON_FORMAT = "[]"; + private static final boolean DEBUG = true; + + public PreparedParametersJSONPerformanceTest() { + super(DEBUG, PreparedParametersJSONPerformanceTest.class.getSimpleName()); + } + + @Override + protected Gson getSlowGson() { + return new GsonBuilder() + .registerTypeAdapterFactory(new PojoTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParameterTypeAdapterFactory()) + .create(); + } + + @Override + protected Gson getFasterGson() { + return new GsonBuilder() + .registerTypeAdapterFactory(new PojoTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParameterTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParametersTypeAdapterFactory()) + .create(); + } + + @Override + protected double getExpectedSpeedup(OperationType type, + int iterations) { + if (type == OperationType.SERIALIZATION && iterations == getColdSerializationIterations()) { + return 2; + } else if (type == OperationType.SERIALIZATION && iterations == getWarmSerializationIterations()) { + // actual should really be approaching 1.2. + return 0.95; + } else if (type == OperationType.DESERIALIZATION && iterations == getColdDeserializationIterations()) { + return 1.5; + } else if (type == OperationType.DESERIALIZATION && iterations == getWarmDeserializationIterations()) { + return 0.95; + } else { + throw new IllegalStateException("bug"); + } + } + + @Override + protected TypeToken<PreparedParameters> getDeserializeTypeToken() { + return TypeToken.get(PreparedParameters.class); + } + + @Override + protected int getColdSerializationIterations() { + return 1; + } + + @Override + protected int getWarmSerializationIterations() { + return 10000; + } + + @Override + protected int getColdDeserializationIterations() { + return 1; + } + + @Override + protected int getWarmDeserializationIterations() { + return 10000; + } + + @Override + protected double getSelfSerializationDelta() { + return 0.3; + } + + @Override + protected double getSelfDeserializationDelta() { + return 0.3; + } + + @Override + protected String mutateJsonString(GsonContext context, int mutator) { + if (context == GsonContext.FASTER) { + return NEW_JSON_FORMAT; + } else if (context == GsonContext.SLOW) { + return OLD_JSON_FORMAT; + } else { + throw new IllegalStateException("bug"); + } + } + + @Override + protected PreparedParameters mutateToBeSerializedInstance(int mutator) { + return new PreparedParameters(0); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/test/java/com/redhat/thermostat/web/common/typeadapters/PreparedParametersTypeAdapterTest.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,145 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Before; +import org.junit.Test; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.redhat.thermostat.storage.core.PreparedParameter; +import com.redhat.thermostat.storage.core.PreparedParameters; +import com.redhat.thermostat.storage.model.AgentInformation; +import com.redhat.thermostat.storage.model.Pojo; + +public class PreparedParametersTypeAdapterTest { + + private Gson gson; + + @Before + public void setup() { + gson = new GsonBuilder() + .registerTypeAdapterFactory(new PojoTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParameterTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParametersTypeAdapterFactory()) + .create(); + } + + @Test + public void canSerializeDeserializeBasic() { + PreparedParameters params = new PreparedParameters(5); + params.setBoolean(0, true); + params.setInt(1, 2300); + params.setLong(2, 2200000000L); + params.setString(3, "testing"); + String[] list = new String[] { + "a", "b", "c" + }; + params.setStringList(4, list); + + String jsonStr = gson.toJson(params, PreparedParameters.class); + PreparedParameters actualParams = gson.fromJson(jsonStr, PreparedParameters.class); + + PreparedParameter[] expected = params.getParams(); + PreparedParameter[] actual = actualParams.getParams(); + + // last element is the string array, which we check manually + for (int i = 0; i < expected.length - 1; i++) { + assertEquals(expected[i].getType(), actual[i].getType()); + assertEquals(expected[i].getValue(), actual[i].getValue()); + } + String actualList[] = (String[])actual[4].getValue(); + for (int i = 0; i < list.length; i++) { + assertEquals(list[i], actualList[i]); + } + } + + @Test + public void canSerializeDeserializeMixedTypesWithPojoList() { + AgentInformation info1 = new AgentInformation("foo-agent"); + info1.setAlive(true); + AgentInformation info2 = new AgentInformation("foo-agent"); + info2.setAlive(false); + info2.setStartTime(System.currentTimeMillis()); + info2.setStopTime(System.currentTimeMillis()); + info2.setConfigListenAddress("127.0.0.1:12000"); + AgentInformation[] infos = new AgentInformation[] { + info1, info2 + }; + long[] longs = new long[] { 3000000000L, -3, 300 }; + // String, long[], Pojo[] + PreparedParameters params = new PreparedParameters(3); + params.setString(0, "foo-param"); + params.setLongList(1, longs); + params.setPojoList(2, infos); + + String jsonStr = gson.toJson(params, PreparedParameters.class); + PreparedParameters actualParams = gson.fromJson(jsonStr, PreparedParameters.class); + + PreparedParameter[] expected = params.getParams(); + PreparedParameter[] actual = actualParams.getParams(); + + assertEquals(expected.length, actual.length); + + PreparedParameter param1 = actual[0]; + assertEquals("foo-param", param1.getValue()); + assertEquals(String.class, param1.getType()); + + PreparedParameter param2 = actual[1]; + assertEquals(long[].class, param2.getType()); + long[] twoActuals = (long[])param2.getValue(); + assertEquals(3, twoActuals.length); + assertEquals(3000000000L, (long)twoActuals[0]); + assertEquals(-3, (long)twoActuals[1]); + assertEquals(300, (long)twoActuals[2]); + + PreparedParameter param3 = actual[2]; + assertEquals(AgentInformation.class, param3.getType()); + assertTrue(param3.getValue().getClass().isArray()); + Pojo[] pojos = (Pojo[])param3.getValue(); + assertEquals(2, pojos.length); + for (int i = 0; i < pojos.length; i++) { + assertEquals(infos[i], pojos[i]); + } + } + + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/test/java/com/redhat/thermostat/web/common/typeadapters/WebPreparedStatementJSONPerformanceTest.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,143 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import org.junit.experimental.categories.Category; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import com.redhat.thermostat.storage.model.Pojo; +import com.redhat.thermostat.testutils.PerformanceTest; +import com.redhat.thermostat.web.common.WebPreparedStatement; +import com.redhat.thermostat.web.common.typeadapters.PojoTypeAdapterFactory; +import com.redhat.thermostat.web.common.typeadapters.WebPreparedStatementTypeAdapterFactory; + +/** + * JUnit categorized performance test. It'll be only run for + * the perf-tests profile during a full build. + */ +@Category(PerformanceTest.class) +@SuppressWarnings("rawtypes") +public class WebPreparedStatementJSONPerformanceTest extends + JsonPerformanceTest<WebPreparedStatement> { + + private static final boolean DEBUG = true; + + public WebPreparedStatementJSONPerformanceTest() { + super(DEBUG, WebPreparedStatementJSONPerformanceTest.class.getSimpleName()); + } + + @Override + protected Gson getSlowGson() { + return new GsonBuilder() + .registerTypeHierarchyAdapter(Pojo.class, new LegacyGSONConverter()) + .registerTypeAdapter(WebPreparedStatement.class, new LegacyWebPreparedStatementSerializer()) + .create(); + } + + @Override + protected Gson getFasterGson() { + return new GsonBuilder() + .registerTypeAdapterFactory(new PojoTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementTypeAdapterFactory()) + .create(); + } + + @Override + protected double getExpectedSpeedup(OperationType type, int iterations) { + if (type == OperationType.SERIALIZATION && iterations == getColdSerializationIterations()) { + return 0.95; + } else if (type == OperationType.SERIALIZATION && iterations == getWarmSerializationIterations()) { + return 1.5; + } else if (type == OperationType.DESERIALIZATION && iterations == getColdDeserializationIterations()) { + return 0.95; + } else if (type == OperationType.DESERIALIZATION && iterations == getWarmDeserializationIterations()) { + return 2; + } else { + throw new IllegalStateException("Bug"); + } + } + + @Override + protected String mutateJsonString(GsonContext ctx, int mutator) { + return String.format("{\"sid\":%d,\"p\":{\"params\":[]}}", mutator); + } + + @Override + protected WebPreparedStatement mutateToBeSerializedInstance(int mutator) { + WebPreparedStatement retval = new WebPreparedStatement<>(0, 555); + retval.setStatementId(mutator); + return retval; + } + + @Override + protected TypeToken<WebPreparedStatement> getDeserializeTypeToken() { + return TypeToken.get(WebPreparedStatement.class); + } + + @Override + protected int getColdSerializationIterations() { + return 1; + } + + @Override + protected int getWarmSerializationIterations() { + return 10000; + } + + @Override + protected int getColdDeserializationIterations() { + return 1; + } + + @Override + protected int getWarmDeserializationIterations() { + return 10000; + } + + @Override + protected double getSelfSerializationDelta() { + return 0.4; + } + + @Override + protected double getSelfDeserializationDelta() { + return 0.1; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/test/java/com/redhat/thermostat/web/common/typeadapters/WebPreparedStatementResponseJSONPerformanceTest.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,144 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import org.junit.experimental.categories.Category; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import com.redhat.thermostat.testutils.PerformanceTest; +import com.redhat.thermostat.web.common.WebPreparedStatementResponse; +import com.redhat.thermostat.web.common.typeadapters.WebPreparedStatementResponseTypeAdapterFactory; + +/** + * JUnit categorized performance test. It'll be only run for + * the perf-tests profile during a full build. + */ +@Category(PerformanceTest.class) +public class WebPreparedStatementResponseJSONPerformanceTest extends + JsonPerformanceTest<WebPreparedStatementResponse> { + + // Set to true in order to turn on debugging output + private static final boolean DEBUG = true; + + public WebPreparedStatementResponseJSONPerformanceTest() { + super(DEBUG, WebPreparedStatementResponseJSONPerformanceTest.class.getSimpleName()); + } + + @Override + protected Gson getSlowGson() { + return new GsonBuilder().create(); + } + + @Override + protected Gson getFasterGson() { + return new GsonBuilder() + .registerTypeAdapterFactory(new WebPreparedStatementResponseTypeAdapterFactory()) + .create(); + } + + @Override + protected double getExpectedSpeedup(OperationType type, int iterations) { + if (type == OperationType.DESERIALIZATION && iterations == getColdDeserializationIterations()) { + return 4; + } else if (type == OperationType.DESERIALIZATION && iterations == getWarmDeserializationIterations()) { + // Actual speed-up of the warmed up scenario is roughly + // >= 1.0. However, there are cases where it falls below it. + // A lower bound of 0.95 should be adequate in order for tests to + // not fail spuriously (at least not as often). + return 0.95; + } else if (type == OperationType.SERIALIZATION && iterations == getColdSerializationIterations()) { + return 4; + } else if (type == OperationType.SERIALIZATION && iterations == getWarmSerializationIterations()) { + return 1.2; + } else { + throw new IllegalStateException("Bug!"); + } + } + + @Override + protected String mutateJsonString(GsonContext ctx, int mutator) { + int stmtId = (mutator + 1) * 20; + int numFreeVars = mutator; + return String.format("{\"numFreeVars\":%d,\"stmtId\":%d}", numFreeVars, stmtId); + } + + @Override + protected WebPreparedStatementResponse mutateToBeSerializedInstance(int mutator) { + WebPreparedStatementResponse response = new WebPreparedStatementResponse(); + response.setNumFreeVariables(3); + response.setStatementId(mutator); + return response; + } + + @Override + protected TypeToken<WebPreparedStatementResponse> getDeserializeTypeToken() { + return TypeToken.get(WebPreparedStatementResponse.class); + } + + @Override + protected int getColdSerializationIterations() { + return 1; + } + + @Override + protected int getWarmSerializationIterations() { + return 10000; + } + + @Override + protected int getColdDeserializationIterations() { + return 1; + } + + @Override + protected int getWarmDeserializationIterations() { + return 5000; + } + + @Override + protected double getSelfSerializationDelta() { + return 0.1; + } + + @Override + protected double getSelfDeserializationDelta() { + return 0.3; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/test/java/com/redhat/thermostat/web/common/typeadapters/WebPreparedStatementResponseTypeAdapterTest.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,107 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.junit.Before; +import org.junit.Test; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonSyntaxException; +import com.redhat.thermostat.web.common.WebPreparedStatementResponse; + +public class WebPreparedStatementResponseTypeAdapterTest { + + private Gson gson; + + @Before + public void setup() { + gson = new GsonBuilder() + .registerTypeAdapterFactory(new WebPreparedStatementResponseTypeAdapterFactory()) + .create(); + } + + @Test + public void testSerializationDeserializationBasic() { + WebPreparedStatementResponse response = new WebPreparedStatementResponse(); + response.setNumFreeVariables(6); + response.setStatementId(WebPreparedStatementResponse.ILLEGAL_STATEMENT); + + String jsonStr = gson.toJson(response, WebPreparedStatementResponse.class); + String expectedString = "{\"numFreeVars\":6,\"stmtId\":-1}"; + assertEquals(expectedString, jsonStr); + + WebPreparedStatementResponse actual = gson.fromJson(jsonStr, WebPreparedStatementResponse.class); + + assertEquals(6, actual.getNumFreeVariables()); + assertEquals(WebPreparedStatementResponse.ILLEGAL_STATEMENT, actual.getStatementId()); + } + + @Test(expected=JsonSyntaxException.class) + public void failDeserializePrimitiveSetNull() { + String jsonString = "{\"numFreeVars\":11,\"stmtId\":null}"; + gson.fromJson(jsonString, WebPreparedStatementResponse.class); + } + + @Test + public void canDeserializeBasic() { + String jsonString = "{\"numFreeVars\":11,\"stmtId\":6}"; + WebPreparedStatementResponse actual = gson.fromJson(jsonString, WebPreparedStatementResponse.class); + assertEquals(6, actual.getStatementId()); + assertEquals(11, actual.getNumFreeVariables()); + } + + @Test(expected=JsonSyntaxException.class) + public void testDeserializationFail() { + String invalidString = "{\"forbar\":6,\"stmtId\":-1}"; + gson.fromJson(invalidString, WebPreparedStatementResponse.class); + } + + @Test + public void canSerializeDeserializeNull() { + WebPreparedStatementResponse response = null; + assertNull(response); + String json = gson.toJson(response, WebPreparedStatementResponse.class); + assertEquals("null", json); + WebPreparedStatementResponse actual = gson.fromJson(json, WebPreparedStatementResponse.class); + assertNull(actual); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/test/java/com/redhat/thermostat/web/common/typeadapters/WebPreparedStatementTypeAdapterTest.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,166 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import org.junit.Before; +import org.junit.Test; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.redhat.thermostat.storage.core.PreparedParameter; +import com.redhat.thermostat.storage.core.PreparedParameters; +import com.redhat.thermostat.storage.model.AgentInformation; +import com.redhat.thermostat.web.common.WebPreparedStatement; +import com.redhat.thermostat.web.common.WebPreparedStatementResponse; + +public class WebPreparedStatementTypeAdapterTest { + + private Gson gson; + + @Before + public void setup() { + gson = new GsonBuilder() + .registerTypeAdapterFactory(new WebPreparedStatementTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParameterTypeAdapterFactory()) + .registerTypeAdapterFactory(new PojoTypeAdapterFactory()) + .create(); + } + + @Test + public void canSerializeNullParams() { + WebPreparedStatement<?> stmt = new WebPreparedStatement<>(); + stmt.setStatementId(500); + stmt.setParams(null); + + String expected = "{\"sid\":500}"; + + String actual = gson.toJson(stmt); + assertEquals(expected, actual); + } + + @Test + public void canDeserializeNullParams() { + String json = "{\"sid\": 500}"; + + WebPreparedStatement<?> stmt = gson.fromJson(json, WebPreparedStatement.class); + assertEquals(500, stmt.getStatementId()); + assertNull(stmt.getParams()); + } + + @Test + public void canSerializeEmptyParams() { + WebPreparedStatement<?> stmt = new WebPreparedStatement<>(0, 555); + + String expected = "{\"sid\":555,\"p\":{\"params\":[]}}"; + String actual = gson.toJson(stmt); + assertEquals(expected, actual); + } + + @Test + public void canDeserializeEmptyParams() { + String json = "{\"sid\":555,\"p\":{\"params\":[]}}"; + + WebPreparedStatement<?> stmt = gson.fromJson(json, WebPreparedStatement.class); + assertEquals(555, stmt.getStatementId()); + assertNotNull(stmt.getParams()); + assertEquals(0, stmt.getParams().getParams().length); + } + + @Test + public void canSerializeAndDeserialize() { + PreparedParameters params = new PreparedParameters(5); + params.setInt(0, 2); + params.setString(1, "testing"); + params.setLong(2, 222L); + params.setStringList(3, new String[] { "one", "two" }); + params.setBoolean(4, true); + WebPreparedStatement<?> stmt = new WebPreparedStatement<>(); + stmt.setParams(params); + stmt.setStatementId(WebPreparedStatementResponse.DESCRIPTOR_PARSE_FAILED); + String jsonString = gson.toJson(stmt, WebPreparedStatement.class); + WebPreparedStatement<?> newStmt = gson.fromJson(jsonString, WebPreparedStatement.class); + assertNotNull(newStmt); + PreparedParameters newParams = newStmt.getParams(); + PreparedParameter[] parameters = newParams.getParams(); + assertEquals(5, parameters.length); + assertEquals(2, parameters[0].getValue()); + assertEquals(int.class, parameters[0].getType()); + assertEquals("testing", parameters[1].getValue()); + assertEquals(String.class, parameters[1].getType()); + assertEquals(222L, parameters[2].getValue()); + 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()); + assertEquals(true, parameters[4].getValue()); + assertEquals(boolean.class, parameters[4].getType()); + assertEquals(WebPreparedStatementResponse.DESCRIPTOR_PARSE_FAILED, newStmt.getStatementId()); + } + + /* + * Writes need Pojo support for serialization. This is a basic test we do + * get Pojos across the wire in a prepared context. + */ + @Test + public void canSerializeDeserializePojoParameters() { + PreparedParameters params = new PreparedParameters(2); + params.setIntList(0, new int[] { 0, 300 }); + AgentInformation pojo1 = new AgentInformation("foo-agent"); + AgentInformation pojo2 = new AgentInformation("foo-agent"); + pojo2.setAlive(true); + pojo2.setConfigListenAddress("127.0.0.1:38822"); + params.setPojoList(1, new AgentInformation[] { pojo1, pojo2 }); + + WebPreparedStatement<?> stmt = new WebPreparedStatement<>(); + stmt.setParams(params); + stmt.setStatementId(WebPreparedStatementResponse.DESCRIPTOR_PARSE_FAILED); + + String jsonString = gson.toJson(stmt, WebPreparedStatement.class); + assertNotNull(jsonString); + + WebPreparedStatement<?> result = gson.fromJson(jsonString, WebPreparedStatement.class); + assertEquals(WebPreparedStatementResponse.DESCRIPTOR_PARSE_FAILED, result.getStatementId()); + assertNotNull(result.getParams()); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/test/java/com/redhat/thermostat/web/common/typeadapters/WebQueryResponseTypeAdapterTest.java Wed Sep 17 11:36:58 2014 +0200 @@ -0,0 +1,189 @@ +/* + * Copyright 2012-2014 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.typeadapters; + +import static org.junit.Assert.assertEquals; + +import java.lang.reflect.Type; + +import org.junit.Before; +import org.junit.Test; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import com.redhat.thermostat.storage.model.AgentInformation; +import com.redhat.thermostat.storage.model.HostInfo; +import com.redhat.thermostat.web.common.PreparedStatementResponseCode; +import com.redhat.thermostat.web.common.WebQueryResponse; +import com.redhat.thermostat.web.common.typeadapters.PojoTypeAdapterFactory; +import com.redhat.thermostat.web.common.typeadapters.WebQueryResponseTypeAdapterFactory; + +public class WebQueryResponseTypeAdapterTest { + + private Gson gson; + + @Before + public void setup() { + gson = new GsonBuilder() + .registerTypeAdapterFactory(new PojoTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebQueryResponseTypeAdapterFactory()) + .create(); + } + + @Test + public void canSerializeBasic() { + // Our test pojo + AgentInformation agentInfo = new AgentInformation("testing"); + agentInfo.setAlive(false); + AgentInformation[] resultList = new AgentInformation[] { + agentInfo + }; + + // create the query response + WebQueryResponse<AgentInformation> response = new WebQueryResponse<>(); + response.setResultList(resultList); + response.setResponseCode(PreparedStatementResponseCode.ILLEGAL_PATCH); + + String jsonStr = gson.toJson(response); + String expectedJson = "{\"errno\":-1,\"payload\":[{\"agentId\":\"testing\",\"alive\":false,\"startTime\":0,\"stopTime\":0}]}"; + assertEquals(expectedJson, jsonStr); + } + + @Test + public void canDeserializeBasic() { + String rawJson = "{\"errno\":-1,\"payload\":[{\"startTime\":0,\"stopTime\":0,\"alive\":true,\"agentId\":\"testing\"}]}"; + Type queryResponseType = new TypeToken<WebQueryResponse<AgentInformation>>() {}.getType(); + WebQueryResponse<AgentInformation> actual = gson.fromJson(rawJson, queryResponseType); + + AgentInformation[] actualList = actual.getResultList(); + + assertEquals(PreparedStatementResponseCode.ILLEGAL_PATCH, actual.getResponseCode()); + assertEquals(1, actualList.length); + AgentInformation actualInfo = actualList[0]; + assertEquals(true, actualInfo.isAlive()); + assertEquals("testing", actualInfo.getAgentId()); + } + + @Test + public void canSerializeAndDeserializeBasic() { + // Our test pojo + AgentInformation agentInfo = new AgentInformation("testing"); + agentInfo.setAlive(false); + AgentInformation[] resultList = new AgentInformation[] { + agentInfo + }; + + // create the query response + WebQueryResponse<AgentInformation> response = new WebQueryResponse<>(); + response.setResultList(resultList); + response.setResponseCode(PreparedStatementResponseCode.ILLEGAL_PATCH); + + String jsonStr = gson.toJson(response); + + // We need to tell GSON which parametrized type we want it to deserialize + // it to. + Type queryResponseType = new TypeToken<WebQueryResponse<AgentInformation>>() {}.getType(); + WebQueryResponse<AgentInformation> actual = gson.fromJson(jsonStr, queryResponseType); + + AgentInformation[] actualList = actual.getResultList(); + + assertEquals(PreparedStatementResponseCode.ILLEGAL_PATCH, actual.getResponseCode()); + assertEquals(1, actualList.length); + AgentInformation actualInfo = actualList[0]; + assertEquals(false, actualInfo.isAlive()); + assertEquals("testing", actualInfo.getAgentId()); + } + + @Test + public void canSerializeAndDeserializeVariousPojos() { + // Our test pojo + AgentInformation agentInfo = new AgentInformation("testing"); + agentInfo.setAlive(false); + AgentInformation[] resultList = new AgentInformation[] { + agentInfo + }; + + // create the query response + WebQueryResponse<AgentInformation> response = new WebQueryResponse<>(); + response.setResultList(resultList); + response.setResponseCode(PreparedStatementResponseCode.ILLEGAL_PATCH); + + String jsonStr = gson.toJson(response); + String expectedJson = "{\"errno\":-1,\"payload\":[{\"agentId\":\"testing\",\"alive\":false,\"startTime\":0,\"stopTime\":0}]}"; + assertEquals(expectedJson, jsonStr); + + // We need to tell GSON which parametrized type we want it to deserialize + // it to. + Type queryResponseType = new TypeToken<WebQueryResponse<AgentInformation>>() {}.getType(); + WebQueryResponse<AgentInformation> actual = gson.fromJson(jsonStr, queryResponseType); + + AgentInformation[] actualList = actual.getResultList(); + + assertEquals(PreparedStatementResponseCode.ILLEGAL_PATCH, actual.getResponseCode()); + assertEquals(1, actualList.length); + AgentInformation actualInfo = actualList[0]; + assertEquals(false, actualInfo.isAlive()); + assertEquals("testing", actualInfo.getAgentId()); + + // Do it again using HostInfo as model + HostInfo hostInfo = new HostInfo(); + hostInfo.setAgentId("something"); + hostInfo.setCpuCount(56); + hostInfo.setHostname("flukebox"); + + HostInfo[] hostInfoResults = new HostInfo[] { + hostInfo + }; + + WebQueryResponse<HostInfo> expected = new WebQueryResponse<>(); + expected.setResultList(hostInfoResults); + expected.setResponseCode(PreparedStatementResponseCode.QUERY_SUCCESS); + + jsonStr = gson.toJson(expected); + Type hostinfoQueryResponseType = new TypeToken<WebQueryResponse<HostInfo>>() {}.getType(); + WebQueryResponse<HostInfo> actualResp = gson.fromJson(jsonStr, hostinfoQueryResponseType); + + assertEquals(PreparedStatementResponseCode.QUERY_SUCCESS, actualResp.getResponseCode()); + HostInfo[] hostInfoList = actualResp.getResultList(); + assertEquals(1, hostInfoList.length); + assertEquals("something", hostInfoList[0].getAgentId()); + assertEquals(56, hostInfoList[0].getCpuCount()); + assertEquals("flukebox", hostInfoList[0].getHostname()); + } +} +
--- a/web/server/src/main/java/com/redhat/thermostat/web/server/WebStorageEndPoint.java Tue Sep 30 11:57:25 2014 +0200 +++ b/web/server/src/main/java/com/redhat/thermostat/web/server/WebStorageEndPoint.java Wed Sep 17 11:36:58 2014 +0200 @@ -98,14 +98,16 @@ import com.redhat.thermostat.storage.query.BinaryLogicalExpression; import com.redhat.thermostat.storage.query.BinaryLogicalOperator; import com.redhat.thermostat.storage.query.Expression; -import com.redhat.thermostat.web.common.PreparedParameterSerializer; import com.redhat.thermostat.web.common.PreparedStatementResponseCode; -import com.redhat.thermostat.web.common.ThermostatGSONConverter; import com.redhat.thermostat.web.common.WebPreparedStatement; import com.redhat.thermostat.web.common.WebPreparedStatementResponse; -import com.redhat.thermostat.web.common.WebPreparedStatementSerializer; import com.redhat.thermostat.web.common.WebQueryResponse; -import com.redhat.thermostat.web.common.WebQueryResponseSerializer; +import com.redhat.thermostat.web.common.typeadapters.PojoTypeAdapterFactory; +import com.redhat.thermostat.web.common.typeadapters.PreparedParameterTypeAdapterFactory; +import com.redhat.thermostat.web.common.typeadapters.PreparedParametersTypeAdapterFactory; +import com.redhat.thermostat.web.common.typeadapters.WebPreparedStatementResponseTypeAdapterFactory; +import com.redhat.thermostat.web.common.typeadapters.WebPreparedStatementTypeAdapterFactory; +import com.redhat.thermostat.web.common.typeadapters.WebQueryResponseTypeAdapterFactory; import com.redhat.thermostat.web.server.auth.FilterResult; import com.redhat.thermostat.web.server.auth.PrincipalCallback; import com.redhat.thermostat.web.server.auth.PrincipalCallbackFactory; @@ -169,11 +171,12 @@ checkThermostatHome(); gson = new GsonBuilder() - .registerTypeHierarchyAdapter(Pojo.class, - new ThermostatGSONConverter()) - .registerTypeAdapter(WebQueryResponse.class, new WebQueryResponseSerializer<>()) - .registerTypeAdapter(PreparedParameter.class, new PreparedParameterSerializer()) - .registerTypeAdapter(WebPreparedStatement.class, new WebPreparedStatementSerializer()) + .registerTypeAdapterFactory(new PojoTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementResponseTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebQueryResponseTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParameterTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParametersTypeAdapterFactory()) .create(); categoryIds = new HashMap<>(); categories = new HashMap<>();
--- a/web/server/src/test/java/com/redhat/thermostat/web/server/WebStorageEndpointTest.java Tue Sep 30 11:57:25 2014 +0200 +++ b/web/server/src/test/java/com/redhat/thermostat/web/server/WebStorageEndpointTest.java Wed Sep 17 11:36:58 2014 +0200 @@ -123,20 +123,21 @@ import com.redhat.thermostat.storage.model.AggregateCount; import com.redhat.thermostat.storage.model.BasePojo; import com.redhat.thermostat.storage.model.HostInfo; -import com.redhat.thermostat.storage.model.Pojo; import com.redhat.thermostat.storage.query.BinarySetMembershipExpression; import com.redhat.thermostat.storage.query.Expression; import com.redhat.thermostat.storage.query.ExpressionFactory; import com.redhat.thermostat.test.FreePortFinder; import com.redhat.thermostat.test.FreePortFinder.TryPort; -import com.redhat.thermostat.web.common.PreparedParameterSerializer; import com.redhat.thermostat.web.common.PreparedStatementResponseCode; -import com.redhat.thermostat.web.common.ThermostatGSONConverter; import com.redhat.thermostat.web.common.WebPreparedStatement; import com.redhat.thermostat.web.common.WebPreparedStatementResponse; -import com.redhat.thermostat.web.common.WebPreparedStatementSerializer; import com.redhat.thermostat.web.common.WebQueryResponse; -import com.redhat.thermostat.web.common.WebQueryResponseSerializer; +import com.redhat.thermostat.web.common.typeadapters.PojoTypeAdapterFactory; +import com.redhat.thermostat.web.common.typeadapters.PreparedParameterTypeAdapterFactory; +import com.redhat.thermostat.web.common.typeadapters.PreparedParametersTypeAdapterFactory; +import com.redhat.thermostat.web.common.typeadapters.WebPreparedStatementResponseTypeAdapterFactory; +import com.redhat.thermostat.web.common.typeadapters.WebPreparedStatementTypeAdapterFactory; +import com.redhat.thermostat.web.common.typeadapters.WebQueryResponseTypeAdapterFactory; import com.redhat.thermostat.web.server.auth.BasicRole; import com.redhat.thermostat.web.server.auth.RolePrincipal; import com.redhat.thermostat.web.server.auth.Roles; @@ -330,10 +331,12 @@ conn.setDoInput(true); conn.setDoOutput(true); Gson gson = new GsonBuilder() - .registerTypeHierarchyAdapter(WebQueryResponse.class, new WebQueryResponseSerializer<>()) - .registerTypeAdapter(Pojo.class, new ThermostatGSONConverter()) - .registerTypeAdapter(WebPreparedStatement.class, new WebPreparedStatementSerializer()) - .registerTypeAdapter(PreparedParameter.class, new PreparedParameterSerializer()).create(); + .registerTypeAdapterFactory(new PojoTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementResponseTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebQueryResponseTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParameterTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementTypeAdapterFactory()) + .create(); OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream()); String body = "query-descriptor=" + URLEncoder.encode(strDescriptor, "UTF-8") + "&category-id=" + categoryId; out.write(body + "\n"); @@ -414,10 +417,13 @@ conn.setDoInput(true); conn.setDoOutput(true); Gson gson = new GsonBuilder() - .registerTypeHierarchyAdapter(WebQueryResponse.class, new WebQueryResponseSerializer<>()) - .registerTypeAdapter(Pojo.class, new ThermostatGSONConverter()) - .registerTypeAdapter(WebPreparedStatement.class, new WebPreparedStatementSerializer()) - .registerTypeAdapter(PreparedParameter.class, new PreparedParameterSerializer()).create(); + .registerTypeAdapterFactory(new PojoTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementResponseTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebQueryResponseTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParameterTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParametersTypeAdapterFactory()) + .create(); OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream()); String body = "query-descriptor=" + URLEncoder.encode(strDescriptor, "UTF-8") + "&category-id=" + categoryId; out.write(body + "\n"); @@ -544,10 +550,13 @@ conn.setDoInput(true); conn.setDoOutput(true); Gson gson = new GsonBuilder() - .registerTypeHierarchyAdapter(WebQueryResponse.class, new WebQueryResponseSerializer<>()) - .registerTypeAdapter(Pojo.class, new ThermostatGSONConverter()) - .registerTypeAdapter(WebPreparedStatement.class, new WebPreparedStatementSerializer()) - .registerTypeAdapter(PreparedParameter.class, new PreparedParameterSerializer()).create(); + .registerTypeAdapterFactory(new PojoTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementResponseTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebQueryResponseTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParameterTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParametersTypeAdapterFactory()) + .create(); OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream()); String body = "query-descriptor=" + URLEncoder.encode(strDescriptor, "UTF-8") + "&category-id=" + categoryId; out.write(body + "\n"); @@ -664,10 +673,13 @@ conn.setDoInput(true); conn.setDoOutput(true); Gson gson = new GsonBuilder() - .registerTypeHierarchyAdapter(WebQueryResponse.class, new WebQueryResponseSerializer<>()) - .registerTypeAdapter(Pojo.class, new ThermostatGSONConverter()) - .registerTypeAdapter(WebPreparedStatement.class, new WebPreparedStatementSerializer()) - .registerTypeAdapter(PreparedParameter.class, new PreparedParameterSerializer()).create(); + .registerTypeAdapterFactory(new PojoTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementResponseTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebQueryResponseTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParameterTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParametersTypeAdapterFactory()) + .create(); OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream()); String body = "query-descriptor=" + URLEncoder.encode(strDescriptor, "UTF-8") + "&category-id=" + categoryId; out.write(body + "\n"); @@ -793,9 +805,12 @@ conn.setDoInput(true); conn.setDoOutput(true); Gson gson = new GsonBuilder() - .registerTypeHierarchyAdapter(Pojo.class, new ThermostatGSONConverter()) - .registerTypeAdapter(WebPreparedStatement.class, new WebPreparedStatementSerializer()) - .registerTypeAdapter(PreparedParameter.class, new PreparedParameterSerializer()).create(); + .registerTypeAdapterFactory(new PojoTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParameterTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementTypeAdapterFactory()) + .registerTypeAdapterFactory(new WebPreparedStatementResponseTypeAdapterFactory()) + .registerTypeAdapterFactory(new PreparedParametersTypeAdapterFactory()) + .create(); OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream()); String body = "query-descriptor=" + URLEncoder.encode(strDescriptor, "UTF-8") + "&category-id=" + categoryId; out.write(body + "\n");