You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by sv...@apache.org on 2017/05/23 14:23:40 UTC

[2/6] brooklyn-server git commit: jclouds TypeCoercions for domain objects

jclouds TypeCoercions for domain objects


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/1b23cb6b
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/1b23cb6b
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/1b23cb6b

Branch: refs/heads/master
Commit: 1b23cb6b6e7471258835b1f0e4820a78f9d5251f
Parents: 57ded18
Author: Aled Sage <al...@gmail.com>
Authored: Mon May 22 14:09:29 2017 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Tue May 23 14:45:33 2017 +0100

----------------------------------------------------------------------
 .../location/jclouds/JcloudsLocation.java       |   4 +-
 .../jclouds/JcloudsLocationResolver.java        |   6 +-
 .../location/jclouds/JcloudsTypeCoercions.java  | 168 +++++++++++
 .../JcloudsTypeCoercionsWithBuilderTest.java    | 250 +++++++++++++++++
 .../JcloudsTypeCoercionsWithCreateTest.java     | 278 +++++++++++++++++++
 5 files changed, 704 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1b23cb6b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java
index de71585..074ec5f 100644
--- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java
@@ -210,7 +210,9 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im
     @SetFromFlag // so it's persisted
     private final Map<MachineLocation,String> vmInstanceIds = Maps.newLinkedHashMap();
 
-    static { Networking.init(); }
+    static {
+        Networking.init();
+    }
 
     public JcloudsLocation() {
         super();

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1b23cb6b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolver.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolver.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolver.java
index aba0a3e..2c2f3ef 100644
--- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolver.java
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolver.java
@@ -50,7 +50,11 @@ public class JcloudsLocationResolver implements LocationResolver {
             // from http://docs.amazonwebservices.com/general/latest/gr/rande.html as of Apr 2012.
             // it is suggested not to maintain this list here, instead to require aws-ec2 explicitly named.
             "eu-west-1","us-east-1","us-west-1","us-west-2","ap-southeast-1","ap-northeast-1","sa-east-1");
-         
+    
+    static {
+        JcloudsTypeCoercions.init();
+    }
+
     private ManagementContext managementContext;
 
     @Override

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1b23cb6b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsTypeCoercions.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsTypeCoercions.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsTypeCoercions.java
new file mode 100644
index 0000000..56534ed
--- /dev/null
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsTypeCoercions.java
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.location.jclouds;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.brooklyn.util.core.flags.MethodCoercions;
+import org.apache.brooklyn.util.core.flags.TypeCoercions;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.javalang.coerce.TryCoercer;
+import org.jclouds.json.SerializedNames;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.common.collect.Sets.SetView;
+import com.google.common.reflect.TypeToken;
+
+public class JcloudsTypeCoercions {
+
+    private JcloudsTypeCoercions() {}
+    
+    private static final AtomicBoolean initialized = new AtomicBoolean(false);
+
+    public static void init() {
+        synchronized (initialized) {
+            if (initialized.compareAndSet(false, true)) {
+                TypeCoercions.registerAdapter(new CoercionFromAutoValueBuilder());
+                TypeCoercions.registerAdapter(new CoercionFromAutoValueCreate());
+            }
+        }
+    }
+    
+    static class CoercionFromAutoValueBuilder implements TryCoercer {
+        @Override
+        public <T> Maybe<T> tryCoerce(Object input, TypeToken<T> targetTypeToken) {
+            Class<? super T> targetType = targetTypeToken.getRawType();
+
+            // If we don't have a map of named params, we can't map it to builder methods
+            if (!(input instanceof Map)) {
+                return null;
+            }
+
+            Optional<Method> builderMethod = findBuilderMethod(targetType);
+            if (!builderMethod.isPresent()) return null;
+            
+            Maybe<?> builder = MethodCoercions.tryFindAndInvokeMultiParameterMethod(targetType, ImmutableList.of(builderMethod.get()), ImmutableList.of());
+            if (builder.isAbsent()) {
+                return (Maybe<T>) (Maybe) builder;
+            }
+
+            Optional<Method> buildMethod = findBuildMethod(builder.get());
+            if (!buildMethod.isPresent()) {
+                return Maybe.absent("Builder for "+targetType.getCanonicalName()+" has no build() method");
+            }
+            
+            for (Map.Entry<?, ?> entry : ((Map<?,?>)input).entrySet()) {
+                String key = (String) entry.getKey();
+                Object val = entry.getValue();
+                
+                Maybe<?> invoked = MethodCoercions.tryFindAndInvokeBestMatchingMethod(builder.get(), key, val);
+                if (invoked.isAbsent()) {
+                    RuntimeException cause = Maybe.Absent.getException(invoked);
+                    return Maybe.absent("Builder for "+targetType.getCanonicalName()+" failed to call method for "+key, cause);
+                }
+            }
+            
+            Maybe<?> result = MethodCoercions.tryFindAndInvokeMultiParameterMethod(builder.get(), ImmutableList.of(buildMethod.get()), ImmutableList.of());
+            if (result.isAbsent()) {
+                RuntimeException cause = Maybe.Absent.getException(result);
+                return Maybe.absent("Builder for "+targetType.getCanonicalName()+" failed to call build method", cause);
+            } else {
+                return (Maybe<T>) (Maybe) result;
+            }
+        }
+        
+        private Optional<Method> findBuilderMethod(Class<?> clazz) {
+            for (Method m: clazz.getMethods()) {
+                if (((m.getModifiers()&Modifier.STATIC) == Modifier.STATIC) && m.getName().equals("builder")
+                        && m.getParameterTypes().length == 0) {
+                    return Optional.of(m);
+                }
+            }
+            return Optional.empty();
+        }
+        
+        private Optional<Method> findBuildMethod(Object instance) {
+            for (Method m: instance.getClass().getMethods()) {
+                if (((m.getModifiers()&Modifier.STATIC) != Modifier.STATIC) && m.getName().equals("build")
+                        && m.getParameterTypes().length == 0) {
+                    return Optional.of(m);
+                }
+            }
+            return Optional.empty();
+        }
+    }
+    
+    static class CoercionFromAutoValueCreate implements TryCoercer {
+        @Override
+        public <T> Maybe<T> tryCoerce(Object input, TypeToken<T> targetTypeToken) {
+            Class<? super T> targetType = targetTypeToken.getRawType();
+            Maybe<?> result = null;
+            Maybe<?> firstError = null;
+
+            // If we don't have a map of named params, we can't map it to "@SerializedNames"?
+            if (!(input instanceof Map)) {
+                return null;
+            }
+
+            Optional<Method> method = findCreateMethod(targetType);
+            if (!method.isPresent()) return null;
+            
+            // Do we have a map of args, and need to look at the "@SerializedNames" annotation?
+            if (method.get().isAnnotationPresent(SerializedNames.class)) {
+                String[] argNames = method.get().getAnnotation(SerializedNames.class).value();
+                
+                SetView<?> extraArgs = Sets.difference(((Map<?,?>)input).keySet(), ImmutableSet.copyOf(argNames));
+                if (!extraArgs.isEmpty()) {
+                    return Maybe.absent("create() for "+targetType.getCanonicalName()+" does not accept extra args "+extraArgs);
+                }
+                
+                List<Object> orderedArgs = Lists.newArrayList();
+                for (String argName : argNames) {
+                    orderedArgs.add(((Map<?,?>)input).get(argName));
+                }
+                result = MethodCoercions.tryFindAndInvokeMultiParameterMethod(targetType, ImmutableList.of(method.get()), orderedArgs);
+                if (result != null && result.isPresent()) return Maybe.of((T)result.get());
+                if (result != null && firstError == null) firstError = result;
+            }
+            
+            //not found
+            if (firstError != null) return (Maybe<T>) (Maybe) firstError;
+            return null;
+        }
+        
+        private Optional<Method> findCreateMethod(Class<?> clazz) {
+            for (Method m: clazz.getMethods()) {
+                if (((m.getModifiers()&Modifier.STATIC) == Modifier.STATIC) && m.getName().equals("create")
+                        && clazz.isAssignableFrom(m.getReturnType())) {
+                    return Optional.of(m);
+                }
+            }
+            return Optional.empty();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1b23cb6b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsTypeCoercionsWithBuilderTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsTypeCoercionsWithBuilderTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsTypeCoercionsWithBuilderTest.java
new file mode 100644
index 0000000..f863102
--- /dev/null
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsTypeCoercionsWithBuilderTest.java
@@ -0,0 +1,250 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.location.jclouds;
+
+import static org.apache.brooklyn.util.core.flags.TypeCoercions.coerce;
+import static org.testng.Assert.assertEquals;
+
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.javalang.coerce.ClassCoercionException;
+import org.testng.annotations.Test;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableMap;
+
+public class JcloudsTypeCoercionsWithBuilderTest {
+
+    static {
+        JcloudsTypeCoercions.init();
+    }
+    
+    @Test
+    public void testCallsBuilder() {
+        assertEquals(
+                coerce(ImmutableMap.of("arg1", "val1", "arg2", "val2"), MyClazz.class), 
+                MyClazz.builder().arg1("val1").arg2("val2").build());
+        assertEquals(
+                coerce(ImmutableMap.of("arg2", "val2", "arg1", "val1"), MyClazz.class), 
+                MyClazz.builder().arg1("val1").arg2("val2").build());
+        assertEquals(
+                coerce(ImmutableMap.of("arg1", "val1"), MyClazz.class), 
+                MyClazz.builder().arg1("val1").build());
+        assertEquals(
+                coerce(ImmutableMap.of("arg2", "val2"), MyClazz.class), 
+                MyClazz.builder().arg2("val2").build());
+    }
+
+    @Test
+    public void testFailsIfExtraArgs() {
+        try {
+            coerce(ImmutableMap.of("arg1", "val1", "arg2", "val2", "arg3", "val3"), MyClazz.class);
+            Asserts.shouldHaveFailedPreviously();
+        } catch (ClassCoercionException e) {
+            Asserts.expectedFailureContains(e, "Builder for", "MyClazz", "failed to call method for arg3");
+        }
+    }
+
+    @Test
+    public void testFailsIfNoBuildMethod() {
+        try {
+            coerce(ImmutableMap.of("arg1", "val1"), MyClazzWithNoBuildMethod.class);
+            Asserts.shouldHaveFailedPreviously();
+        } catch (ClassCoercionException e) {
+            Asserts.expectedFailureContains(e, "Builder for", "MyClazzWithNoBuildMethod", "has no build() method");
+        }
+    }
+
+    @Test
+    public void testFailsIfNoNoargBuildMethod() {
+        try {
+            coerce(ImmutableMap.of("arg1", "val1"), MyClazzWithNoNoargBuildMethod.class);
+            Asserts.shouldHaveFailedPreviously();
+        } catch (ClassCoercionException e) {
+            Asserts.expectedFailureContains(e, "Builder for", "MyClazzWithNoNoargBuildMethod", "has no build() method");
+        }
+    }
+
+    @Test
+    public void testFailsIfNoNoargBuilderMethod() {
+        try {
+            coerce(ImmutableMap.of("arg1", "val1"), MyClazzWithNoNoargBuilderMethod.class);
+            Asserts.shouldHaveFailedPreviously();
+        } catch (ClassCoercionException e) {
+            Asserts.expectedFailureContains(e, "MyClazzWithNoNoargBuilderMethod", "no adapter known");
+        }
+    }
+
+    @Test
+    public void testCompositeOfCreatedObjs() {
+        assertEquals(
+                coerce(ImmutableMap.of("val",ImmutableMap.of("arg1", "val1", "arg2", "val2")), MyCompositeClazz.class), 
+                MyCompositeClazz.builder().val((MyClazz.builder().arg1("val1").arg2("val2").build())).build());
+    }
+
+    public static class MyClazz {
+        private final String arg1;
+        private final String arg2;
+        
+        public static class Builder {
+            private String arg1;
+            private String arg2;
+            
+            public Builder arg1(String val) {
+                this.arg1 = val;
+                return this;
+            }
+            public Builder arg2(String val) {
+                this.arg2 = val;
+                return this;
+            }
+            public MyClazz build() {
+                return new MyClazz(arg1, arg2);
+            }
+        }
+            
+        public static Builder builder() {
+            return new Builder();
+        }
+        
+        private MyClazz(String arg1, String arg2) {
+            this.arg1 = arg1;
+            this.arg2 = arg2;
+        }
+        
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == null || obj.getClass() != getClass()) return false;
+            MyClazz o = (MyClazz) obj;
+            return Objects.equal(arg1, o.arg1) && Objects.equal(arg2, o.arg2);
+        }
+        
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(arg1, arg2);
+        }
+        
+        @Override
+        public String toString() {
+            return MoreObjects.toStringHelper(this).add("arg1", arg1).add("arg2", arg2).toString();
+        }
+    }
+    
+    public static class MyCompositeClazz {
+        private final MyClazz val;
+        
+        public static class Builder {
+            private MyClazz val;
+            
+            public Builder val(MyClazz val) {
+                this.val = val;
+                return this;
+            }
+            public MyCompositeClazz build() {
+                return new MyCompositeClazz(val);
+            }
+        }
+            
+        public static Builder builder() {
+            return new Builder();
+        }
+        
+        private MyCompositeClazz(MyClazz val) {
+            this.val = val;
+        }
+        
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == null || obj.getClass() != getClass()) return false;
+            MyCompositeClazz o = (MyCompositeClazz) obj;
+            return Objects.equal(val, o.val);
+        }
+        
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(val);
+        }
+        
+        @Override
+        public String toString() {
+            return MoreObjects.toStringHelper(this).add("val", val).toString();
+        }
+    }
+    
+    public static class MyClazzWithNoNoargBuilderMethod {
+        @SuppressWarnings("unused")
+        private final String arg1;
+        
+        public static class Builder {
+            private String arg1;
+            
+            public Builder arg1(String val) {
+                this.arg1 = val;
+                return this;
+            }
+            public MyClazzWithNoNoargBuilderMethod build() {
+                return new MyClazzWithNoNoargBuilderMethod(arg1);
+            }
+        }
+            
+        public static Builder builder(String extraArg) {
+            return new Builder();
+        }
+        
+        private MyClazzWithNoNoargBuilderMethod(String arg1) {
+            this.arg1 = arg1;
+        }
+    }
+
+    public static class MyClazzWithNoBuildMethod {
+        public static class Builder {
+            public Builder arg1(String val) {
+                return this;
+            }
+        }
+            
+        public static Builder builder() {
+            return new Builder();
+        }
+        
+        private MyClazzWithNoBuildMethod(String arg1) {
+        }
+    }
+    
+    public static class MyClazzWithNoNoargBuildMethod {
+        public static class Builder {
+            private String arg1;
+            
+            public Builder arg1(String val) {
+                this.arg1 = val;
+                return this;
+            }
+            public MyClazzWithNoNoargBuildMethod build(String extraArg) {
+                return new MyClazzWithNoNoargBuildMethod(arg1);
+            }
+        }
+            
+        public static Builder builder() {
+            return new Builder();
+        }
+        
+        private MyClazzWithNoNoargBuildMethod(String arg1) {
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1b23cb6b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsTypeCoercionsWithCreateTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsTypeCoercionsWithCreateTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsTypeCoercionsWithCreateTest.java
new file mode 100644
index 0000000..8adab07
--- /dev/null
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsTypeCoercionsWithCreateTest.java
@@ -0,0 +1,278 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.location.jclouds;
+
+import static org.apache.brooklyn.util.core.flags.TypeCoercions.coerce;
+import static org.testng.Assert.assertEquals;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.javalang.coerce.ClassCoercionException;
+import org.jclouds.json.SerializedNames;
+import org.testng.annotations.Test;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class JcloudsTypeCoercionsWithCreateTest {
+
+    static {
+        JcloudsTypeCoercions.init();
+    }
+    
+    @Test
+    public void testCallsCreate() {
+        assertEquals(
+                coerce(ImmutableMap.of("arg1", "val1", "arg2", "val2"), MyClazz.class), 
+                MyClazz.create("val1", "val2"));
+        assertEquals(
+                coerce(ImmutableMap.of("arg2", "val2", "arg1", "val1"), MyClazz.class), 
+                MyClazz.create("val1", "val2"));
+        assertEquals(
+                coerce(ImmutableMap.of("arg1", "val1"), MyClazz.class), 
+                MyClazz.create("val1", null));
+        assertEquals(
+                coerce(ImmutableMap.of("arg2", "val2"), MyClazz.class), 
+                MyClazz.create(null, "val2"));
+    }
+
+    @Test
+    public void testFailsIfExtraArgs() {
+        try {
+            coerce(ImmutableMap.of("arg1", "val1", "arg2", "val2", "arg3", "val3"), MyClazz.class);
+            Asserts.shouldHaveFailedPreviously();
+        } catch (ClassCoercionException e) {
+            Asserts.expectedFailureContains(e, "create()", "MyClazz", "does not accept extra args [arg3]");
+        }
+    }
+
+    @Test
+    public void testCallsCreateWithPrimitives() {
+        assertEquals(
+                coerce(ImmutableMap.builder().put("boolArg", true).put("byteArg", (byte)1).put("shortArg", (short)2)
+                        .put("intArg", (int)3).put("longArg", (long)4).put("floatArg", (float)5.0)
+                        .put("doubleArg", (double)6.0).build(), MyClazzWithPrimitives.class), 
+                MyClazzWithPrimitives.create(true, (byte)1, (short)2, (int)3, (long)4, (float)5.0, (double)6.0));
+        assertEquals(
+                coerce(ImmutableMap.builder().put("boolArg", "true").put("byteArg", "1").put("shortArg", "2")
+                        .put("intArg", "3").put("longArg", "4").put("floatArg", "5.0")
+                        .put("doubleArg", "6.0").build(), MyClazzWithPrimitives.class), 
+                MyClazzWithPrimitives.create(true, (byte)1, (short)2, (int)3, (long)4, (float)5.0, (double)6.0));
+    }
+    @Test
+    public void testListOfCreatedObjs() {
+        assertEquals(
+                coerce(ImmutableMap.of("vals", ImmutableList.of()), ListOfMyClazz.class), 
+                ListOfMyClazz.create(ImmutableList.of()));
+        assertEquals(
+                coerce(ImmutableMap.of("vals", ImmutableList.of(ImmutableMap.of("arg1", "val1", "arg2", "val2"))), ListOfMyClazz.class), 
+                ListOfMyClazz.create(ImmutableList.of(MyClazz.create("val1", "val2"))));
+    }
+
+    @Test
+    public void testCompositeOfCreatedObjs() {
+        assertEquals(
+                coerce(ImmutableMap.of("val",ImmutableMap.of("arg1", "val1", "arg2", "val2")), MyCompositeClazz.class), 
+                MyCompositeClazz.create(MyClazz.create("val1", "val2")));
+    }
+    
+    @Test
+    public void testMapOfCreatedObjs() {
+        assertEquals(
+                coerce(ImmutableMap.of("vals", ImmutableMap.of()), MapOfMyClazz.class), 
+                MapOfMyClazz.create(ImmutableMap.of()));
+        assertEquals(
+                coerce(ImmutableMap.of("vals", ImmutableMap.of("key1", ImmutableMap.of("arg1", "val1", "arg2", "val2"))), MapOfMyClazz.class), 
+                MapOfMyClazz.create(ImmutableMap.of("key1", MyClazz.create("val1", "val2"))));
+    }
+
+    public static class MyClazz {
+        private final String arg1;
+        private final String arg2;
+        
+        @SerializedNames({"arg1", "arg2"})
+        public static MyClazz create(String arg1, String arg2) {
+            return new MyClazz(arg1, arg2);
+        }
+        
+        private MyClazz(String arg1, String arg2) {
+            this.arg1 = arg1;
+            this.arg2 = arg2;
+        }
+        
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof MyClazz)) return false;
+            MyClazz o = (MyClazz) obj;
+            return Objects.equal(arg1, o.arg1) && Objects.equal(arg2, o.arg2);
+        }
+        
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(arg1, arg2);
+        }
+        
+        @Override
+        public String toString() {
+            return MoreObjects.toStringHelper(this).add("arg1", arg1).add("arg2", arg2).toString();
+        }
+    }
+    
+    public static class MyCompositeClazz {
+        private final MyClazz val;
+        
+        @SerializedNames({"val"})
+        public static MyCompositeClazz create(MyClazz val) {
+            return new MyCompositeClazz(val);
+        }
+        
+        private MyCompositeClazz(MyClazz val) {
+            this.val = val;
+        }
+        
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof MyCompositeClazz)) return false;
+            MyCompositeClazz o = (MyCompositeClazz) obj;
+            return Objects.equal(val, o.val);
+        }
+        
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(val);
+        }
+        
+        @Override
+        public String toString() {
+            return MoreObjects.toStringHelper(this).add("val", val).toString();
+        }
+    }
+    
+    public static class MyClazzWithPrimitives {
+        private final boolean boolArg;
+        private final byte byteArg;
+        private final short shortArg;
+        private final int intArg;
+        private final long longArg;
+        private final float floatArg;
+        private final double doubleArg;
+        
+        @SerializedNames({"boolArg", "byteArg", "shortArg", "intArg", "longArg", "floatArg", "doubleArg"})
+        public static MyClazzWithPrimitives create(boolean boolArg, byte byteArg, short shortArg, int intArg, long longArg, float floatArg, double doubleArg) {
+            return new MyClazzWithPrimitives(boolArg, byteArg, shortArg, intArg, longArg, floatArg, doubleArg);
+        }
+        
+        private MyClazzWithPrimitives(boolean boolArg, byte byteArg, short shortArg, int intArg, long longArg, float floatArg, double doubleArg) {
+            this.boolArg = boolArg;
+            this.byteArg = byteArg;
+            this.shortArg = shortArg;
+            this.intArg = intArg;
+            this.longArg = longArg;
+            this.floatArg = floatArg;
+            this.doubleArg = doubleArg;
+        }
+        
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof MyClazzWithPrimitives)) return false;
+            MyClazzWithPrimitives o = (MyClazzWithPrimitives) obj;
+            return Objects.equal(boolArg, o.boolArg) && Objects.equal(byteArg, o.byteArg)
+                    && Objects.equal(shortArg, o.shortArg) && Objects.equal(intArg, o.intArg)
+                    && Objects.equal(longArg, o.longArg) && Objects.equal(floatArg, o.floatArg)
+                    && Objects.equal(doubleArg, o.doubleArg);
+        }
+        
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(boolArg, byteArg, shortArg, intArg, longArg, floatArg, doubleArg);
+        }
+        
+        @Override
+        public String toString() {
+            return MoreObjects.toStringHelper(this).add("boolArg", boolArg).add("byteArg", byteArg)
+                    .add("shortArg", shortArg).add("intArg", intArg).add("longArg", longArg)
+                    .add("floatArg", floatArg).add("doubleArg", doubleArg)
+                    .toString();
+        }
+    }
+    
+    public static class ListOfMyClazz {
+        private final List<MyClazz> vals;
+        
+        @SerializedNames({"vals"})
+        public static ListOfMyClazz create(List<MyClazz> vals) {
+            return new ListOfMyClazz(vals);
+        }
+        
+        private ListOfMyClazz(List<MyClazz> vals) {
+            this.vals = vals;
+        }
+        
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof ListOfMyClazz)) return false;
+            ListOfMyClazz o = (ListOfMyClazz) obj;
+            return Objects.equal(vals, o.vals);
+        }
+        
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(vals);
+        }
+        
+        @Override
+        public String toString() {
+            return MoreObjects.toStringHelper(this).add("vals", vals).toString();
+        }
+    }
+    
+    public static class MapOfMyClazz {
+        private final Map<String, MyClazz> vals;
+        
+        @SerializedNames({"vals"})
+        public static MapOfMyClazz create(Map<String, MyClazz> vals) {
+            return new MapOfMyClazz(vals);
+        }
+        
+        private MapOfMyClazz(Map<String, MyClazz> vals) {
+            this.vals = vals;
+        }
+        
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof MapOfMyClazz)) return false;
+            MapOfMyClazz o = (MapOfMyClazz) obj;
+            return Objects.equal(vals, o.vals);
+        }
+        
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(vals);
+        }
+        
+        @Override
+        public String toString() {
+            return MoreObjects.toStringHelper(this).add("vals", vals).toString();
+        }
+    }
+}