From c0e6a2c51cd131bcef2ee051cfe200b41afda528 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Sun, 26 Oct 2014 11:44:48 -0700 Subject: [PATCH] Fixed bug found in docker provider where deserialization constructors don't handle json null. --- ...ructorAndReflectiveTypeAdapterFactory.java | 2 +- .../java/org/jclouds/reflect/Reflection2.java | 5 +- .../test/java/org/jclouds/json/JsonTest.java | 61 +++++++++++-------- 3 files changed, 39 insertions(+), 29 deletions(-) diff --git a/core/src/main/java/org/jclouds/json/internal/DeserializationConstructorAndReflectiveTypeAdapterFactory.java b/core/src/main/java/org/jclouds/json/internal/DeserializationConstructorAndReflectiveTypeAdapterFactory.java index e00f57c0c9..87e9d32846 100644 --- a/core/src/main/java/org/jclouds/json/internal/DeserializationConstructorAndReflectiveTypeAdapterFactory.java +++ b/core/src/main/java/org/jclouds/json/internal/DeserializationConstructorAndReflectiveTypeAdapterFactory.java @@ -166,7 +166,7 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactory imp empty = false; String name = in.nextName(); ParameterReader parameter = parameterReaders.get(name); - if (parameter == null) { + if (parameter == null || in.peek() == JsonToken.NULL) { in.skipValue(); } else { Object value = parameter.read(in); diff --git a/core/src/main/java/org/jclouds/reflect/Reflection2.java b/core/src/main/java/org/jclouds/reflect/Reflection2.java index 03db79fce6..bfd39b46be 100644 --- a/core/src/main/java/org/jclouds/reflect/Reflection2.java +++ b/core/src/main/java/org/jclouds/reflect/Reflection2.java @@ -128,8 +128,8 @@ public class Reflection2 { } /** - * This gets all declared constructors, not just public ones. makes them accessible, as well. - * This also includes factory methods on abstract types, defined static methods returning the same type. + * This gets all declared constructors or factory methods on abstract types, not just public ones, and makes them + * accessible. */ private static LoadingCache, Set>> constructorsForTypeToken = CacheBuilder .newBuilder().build(new CacheLoader, Set>>() { @@ -143,6 +143,7 @@ public class Reflection2 { if (Modifier.isAbstract(key.getRawType().getModifiers())) { for (Invokable method : methods(key.getRawType())){ if (method.isStatic() && method.getReturnType().equals(key)) { + method.setAccessible(true); builder.add(method); } } diff --git a/core/src/test/java/org/jclouds/json/JsonTest.java b/core/src/test/java/org/jclouds/json/JsonTest.java index ed322200dc..3a559dbc00 100644 --- a/core/src/test/java/org/jclouds/json/JsonTest.java +++ b/core/src/test/java/org/jclouds/json/JsonTest.java @@ -20,14 +20,17 @@ import static com.google.common.io.BaseEncoding.base16; import static com.google.common.primitives.Bytes.asList; import static org.testng.Assert.assertEquals; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Properties; +import org.jclouds.javax.annotation.Nullable; import org.jclouds.json.config.GsonModule; import org.jclouds.json.config.GsonModule.DefaultExclusionStrategy; import org.testng.annotations.Test; +import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.gson.FieldAttributes; @@ -210,67 +213,73 @@ public class JsonTest { EnumInsideWithParser.Test.UNRECOGNIZED); } - private abstract static class SpinalCasedType { + private abstract static class UpperCamelCasedType { abstract String id(); - abstract String contentType(); + @Nullable abstract Map volumes(); // Currently, this only works for deserialization. Need to set a naming policy for serialization! - @SerializedNames({ "id", "content-type" }) - private static SpinalCasedType create(String id, String contentType) { - return new SpinalCasedTypeImpl(id, contentType); + @SerializedNames({ "Id", "Volumes" }) + private static UpperCamelCasedType create(String id, Map volumes) { + return new UpperCamelCasedTypeImpl(id, volumes); } } - public void spinalCaseWithSerializedNames() { + public void upperCamelCaseWithSerializedNames() { Json json = Guice.createInjector(new GsonModule(), new AbstractModule() { @Override protected void configure() { - bind(FieldNamingPolicy.class).toInstance(FieldNamingPolicy.LOWER_CASE_WITH_DASHES); + bind(FieldNamingPolicy.class).toInstance(FieldNamingPolicy.UPPER_CAMEL_CASE); } }).getInstance(Json.class); - SpinalCasedType resource = SpinalCasedType.create("1234", "application/json"); - String spinalJson = "{\"id\":\"1234\",\"content-type\":\"application/json\"}"; + UpperCamelCasedType resource = UpperCamelCasedType.create("1234", Collections.emptyMap()); + String spinalJson = "{\"Id\":\"1234\",\"Volumes\":{}}"; assertEquals(json.toJson(resource), spinalJson); - assertEquals(spinalJson, json.toJson(resource)); + assertEquals(json.fromJson(spinalJson, UpperCamelCasedType.class), resource); } - private static class SpinalCasedTypeImpl extends SpinalCasedType { - private final String id; - private final String contentType; + public void upperCamelCaseWithSerializedNamesNullJsonValue() { + Json json = Guice.createInjector(new GsonModule(), new AbstractModule() { + @Override protected void configure() { + bind(FieldNamingPolicy.class).toInstance(FieldNamingPolicy.UPPER_CAMEL_CASE); + } + }).getInstance(Json.class); - private SpinalCasedTypeImpl(String id, String contentType) { + assertEquals(json.fromJson("{\"Id\":\"1234\",\"Volumes\":null}", UpperCamelCasedType.class), + UpperCamelCasedType.create("1234", null)); + } + + private static class UpperCamelCasedTypeImpl extends UpperCamelCasedType { + private final String id; + private final Map volumes; + + private UpperCamelCasedTypeImpl(String id, Map volumes) { this.id = id; - this.contentType = contentType; + this.volumes = volumes; } @Override String id() { return id; } - @Override String contentType() { - return contentType; + @Override @Nullable Map volumes() { + return volumes; } @Override public boolean equals(Object o) { if (o == this) { return true; } - if (o instanceof SpinalCasedType) { - SpinalCasedType that = (SpinalCasedType) o; - return (this.id.equals(that.id())) && (this.contentType.equals(that.contentType())); + if (o instanceof UpperCamelCasedType) { + UpperCamelCasedType that = (UpperCamelCasedType) o; + return Objects.equal(this.id, that.id()) && Objects.equal(this.volumes, that.volumes()); } return false; } @Override public int hashCode() { - int h = 1; - h *= 1000003; - h ^= id.hashCode(); - h *= 1000003; - h ^= contentType.hashCode(); - return h; + return Objects.hashCode(id, volumes); } } }