You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by al...@apache.org on 2017/03/10 13:51:16 UTC

[4/5] brooklyn-server git commit: $brooklyn:object - support coercing args

$brooklyn:object - support coercing args

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

Branch: refs/heads/master
Commit: 7ddca12d489796bd916b1f8b70586b93dbdaabfa
Parents: acfcb91
Author: Aled Sage <al...@gmail.com>
Authored: Wed Mar 8 22:11:16 2017 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Mar 8 23:45:41 2017 +0000

----------------------------------------------------------------------
 .../spi/dsl/methods/BrooklynDslCommon.java      |  5 +-
 .../brooklyn/camp/brooklyn/ObjectsYamlTest.java | 18 +++++
 .../brooklyn/util/core/flags/TypeCoercions.java | 14 ++++
 .../brooklyn/util/javalang/Reflections.java     | 77 ++++++++++++++++----
 .../brooklyn/util/javalang/ReflectionsTest.java | 21 +++++-
 5 files changed, 120 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/7ddca12d/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
index 2bc8878..9fbab0e 100644
--- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
+++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
@@ -63,6 +63,7 @@ import org.apache.brooklyn.util.core.task.Tasks;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.javalang.Reflections;
+import org.apache.brooklyn.util.javalang.coerce.TypeCoercer;
 import org.apache.brooklyn.util.net.Urls;
 import org.apache.commons.beanutils.BeanUtils;
 import org.slf4j.Logger;
@@ -70,6 +71,7 @@ import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Function;
 import com.google.common.base.Objects;
+import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
@@ -697,7 +699,8 @@ public class BrooklynDslCommon {
 
         public static Object create(Class<?> type, String factoryMethodName, List<?> factoryMethodArgs, Map<String,?> fields, Map<String,?> config) {
             try {
-                Object bean = Reflections.invokeMethodFromArgs(type, factoryMethodName, factoryMethodArgs).get();
+                Optional<TypeCoercer> coercer = Optional.of(TypeCoercions.asTypeCoercer());
+                Object bean = Reflections.invokeMethodFromArgs(type, factoryMethodName, factoryMethodArgs, false, coercer).get();
                 BeanUtils.populate(bean, fields);
 
                 if (config.size() > 0) {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/7ddca12d/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ObjectsYamlTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ObjectsYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ObjectsYamlTest.java
index 01aa477..ec9937c 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ObjectsYamlTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ObjectsYamlTest.java
@@ -40,6 +40,7 @@ import org.apache.brooklyn.util.collections.MutableSet;
 import org.apache.brooklyn.util.core.config.ConfigBag;
 import org.apache.brooklyn.util.core.flags.SetFromFlag;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;
+import org.apache.brooklyn.util.time.Time;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
@@ -397,6 +398,23 @@ public class ObjectsYamlTest extends AbstractYamlTest {
         assertEquals(testObject.getNumber(), Integer.valueOf(1));
         assertEquals(testObject.getObject(), "myDefaultThird");
     }
+    
+    @Test
+    public void testBrooklynObjectWithFactoryMethodWithArgCoercion() throws Exception {
+        Entity testEntity = setupAndCheckTestEntityInBasicYamlWith(
+            "  brooklyn.config:",
+            "    test.confObject:",
+            "      $brooklyn:object:",
+            "        type: " + Time.class.getName(),
+            "        factoryMethod.name: makeDateString",
+            "        factoryMethod.args:",
+            "        - 1000",
+            "        - yyyy-MM-dd'T'HH:mm:ss",
+            "        - UTC");
+
+        String val = (String) testEntity.getConfig(TestEntity.CONF_OBJECT);
+        assertEquals(val, "1970-01-01T00:00:01");
+    }
 
     @Test
     public void testFieldsAsDeferredSuppliers() throws Exception {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/7ddca12d/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java b/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java
index 923a082..29d8d4b 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java
@@ -254,4 +254,18 @@ public class TypeCoercions {
         }
         
     }
+
+    public static TypeCoercer asTypeCoercer() {
+        return new TypeCoercer() {
+            @Override public <T> T coerce(Object input, Class<T> type) {
+                return TypeCoercions.coerce(input, type);
+            }
+            @Override public <T> Maybe<T> tryCoerce(Object input, Class<T> type) {
+                return TypeCoercions.tryCoerce(input, type);
+            }
+            @Override public <T> Maybe<T> tryCoerce(Object input, TypeToken<T> type) {
+                return TypeCoercions.tryCoerce(input, type);
+            }
+        };
+    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/7ddca12d/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Reflections.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Reflections.java b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Reflections.java
index fba72b5..abcaf47 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Reflections.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Reflections.java
@@ -48,6 +48,7 @@ import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.javalang.coerce.TypeCoercer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -840,19 +841,33 @@ public class Reflections {
     public static Maybe<Object> invokeMethodFromArgs(Object clazzOrInstance, String method, List<?> args) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
         return invokeMethodFromArgs(clazzOrInstance, method, args, false);
     }
+    
     /** as {@link #invokeMethodFromArgs(Object, String, List)} but giving control over whether to set it accessible */
     public static Maybe<Object> invokeMethodFromArgs(Object clazzOrInstance, String method, List<?> args, boolean setAccessible) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+        return invokeMethodFromArgs(clazzOrInstance, method, args, setAccessible, Optional.<TypeCoercer>absent());
+    }
+    
+    /** as {@link #invokeMethodFromArgs(Object, String, List)} but giving control over whether to set it accessible */
+    public static Maybe<Object> invokeMethodFromArgs(Object clazzOrInstance, String method, List<?> args, boolean setAccessible, Optional<? extends TypeCoercer> coercer) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
         Maybe<Method> maybeMethod = getMethodFromArgs(clazzOrInstance, method, args);
+        if (coercer.isPresent() && maybeMethod.isAbsent()) {
+            maybeMethod = getMethodFromArgs(clazzOrInstance, method, args, coercer);
+        }
         if (maybeMethod.isAbsent()) {
             return Maybe.absent(Maybe.getException(maybeMethod));
         }
         Method m = maybeMethod.get();
 
-        return Maybe.of(invokeMethodFromArgs(clazzOrInstance, m, args, setAccessible));
+        return Maybe.of(invokeMethodFromArgs(clazzOrInstance, m, args, setAccessible, coercer));
     }
 
     /** searches for the given method on the given clazz or instance, doing reasonably good matching on args etc */
     public static Maybe<Method> getMethodFromArgs(Object clazzOrInstance, String method, List<?> args) {
+        return getMethodFromArgs(clazzOrInstance, method, args, Optional.<TypeCoercer>absent());
+    }
+    
+    /** searches for the given method on the given clazz or instance, doing reasonably good matching on args etc */
+    public static Maybe<Method> getMethodFromArgs(Object clazzOrInstance, String method, List<?> args, Optional<? extends TypeCoercer> coercer) {
         Preconditions.checkNotNull(clazzOrInstance, "clazz or instance");
         Preconditions.checkNotNull(method, "method");
         Preconditions.checkNotNull(args, "args to "+method);
@@ -870,12 +885,11 @@ public class Reflections {
             if (method.equals(m.getName())) {
                 Class<?>[] parameterTypes = m.getParameterTypes();
                 if (m.isVarArgs()) {
-                    if (typesMatchUpTo(argsArray, parameterTypes, parameterTypes.length-1)) {
+                    if (typesMatchUpTo(argsArray, parameterTypes, parameterTypes.length-1, coercer)) {
                         Class<?> varargType = parameterTypes[parameterTypes.length-1].getComponentType();
                         boolean varargsMatch = true;
                         for (int i=parameterTypes.length-1; i<argsArray.length; i++) {
-                            if (!Boxing.boxedType(varargType).isInstance(argsArray[i]) ||
-                                    (varargType.isPrimitive() && argsArray[i]==null)) {
+                            if (!typeMatches(argsArray[i], varargType, coercer)) {
                                 varargsMatch = false;
                                 break;
                             }
@@ -885,7 +899,7 @@ public class Reflections {
                         }
                     }
                 }
-                if (typesMatch(argsArray, parameterTypes)) {
+                if (typesMatch(argsArray, parameterTypes, coercer)) {
                     return Maybe.of(m);
                 }
             }
@@ -910,6 +924,12 @@ public class Reflections {
     /** as {@link #invokeMethodFromArgs(Object, Method, List)} but giving control over whether to set it accessible */
     public static Object invokeMethodFromArgs(Object clazzOrInstance, Method m, List<?> args, boolean setAccessible)
             throws IllegalAccessException, InvocationTargetException {
+        return invokeMethodFromArgs(clazzOrInstance, m, args, setAccessible, Optional.<TypeCoercer>absent());
+    }
+
+    /** as {@link #invokeMethodFromArgs(Object, Method, List)} but giving control over whether to set it accessible */
+    public static Object invokeMethodFromArgs(Object clazzOrInstance, Method m, List<?> args, boolean setAccessible, Optional<? extends TypeCoercer> coercer)
+            throws IllegalAccessException, InvocationTargetException {
         Preconditions.checkNotNull(clazzOrInstance, "clazz or instance");
         Preconditions.checkNotNull(m, "method");
         Preconditions.checkNotNull(args, "args to "+m);
@@ -928,38 +948,69 @@ public class Reflections {
             Class<?> varargType = parameterTypes[parameterTypes.length-1].getComponentType();
             Object varargs = Array.newInstance(varargType, argsArray.length+1 - parameterTypes.length);
             for (int i=parameterTypes.length-1; i<argsArray.length; i++) {
-                Boxing.setInArray(varargs, i+1-parameterTypes.length, argsArray[i], varargType);
+                Object arg = coercer.isPresent() ? coercer.get().coerce(argsArray[i], varargType) : argsArray[i];
+                Boxing.setInArray(varargs, i+1-parameterTypes.length, arg, varargType);
             }
             Object[] newArgsArray = new Object[parameterTypes.length];
-            System.arraycopy(argsArray, 0, newArgsArray, 0, parameterTypes.length-1);
+            for (int i = 0; i < parameterTypes.length - 1; i++) {
+                Object arg = coercer.isPresent() ? coercer.get().coerce(argsArray[i], parameterTypes[i]) : argsArray[i];
+                newArgsArray[i] = arg;
+            }
             newArgsArray[parameterTypes.length-1] = varargs;
             if (setAccessible) m.setAccessible(true);
             return m.invoke(instance, newArgsArray);
         } else {
+            Object[] newArgsArray;
+            if (coercer.isPresent()) {
+                newArgsArray = new Object[parameterTypes.length];
+                for (int i = 0; i < parameterTypes.length; i++) {
+                    Object arg = coercer.get().coerce(argsArray[i], parameterTypes[i]);
+                    newArgsArray[i] = arg;
+                }
+            } else {
+                newArgsArray = argsArray;
+            }
             if (setAccessible) m.setAccessible(true);
-            return m.invoke(instance, argsArray);
+            return m.invoke(instance, newArgsArray);
         }
     }
 
     /** true iff all args match the corresponding types */
     public static boolean typesMatch(Object[] argsArray, Class<?>[] parameterTypes) {
+        return typesMatch(argsArray, parameterTypes, Optional.<TypeCoercer>absent());
+    }
+    
+    /** true iff all args match the corresponding types */
+    public static boolean typesMatch(Object[] argsArray, Class<?>[] parameterTypes, Optional<? extends TypeCoercer> coercer) {
         if (argsArray.length != parameterTypes.length)
             return false;
-        return typesMatchUpTo(argsArray, parameterTypes, argsArray.length);
+        return typesMatchUpTo(argsArray, parameterTypes, argsArray.length, coercer);
     }
-    
+
     /** true iff the initial N args match the corresponding types */
     public static boolean typesMatchUpTo(Object[] argsArray, Class<?>[] parameterTypes, int lengthRequired) {
+        return typesMatchUpTo(argsArray, parameterTypes, lengthRequired, Optional.<TypeCoercer>absent());
+    }
+    
+    /** true iff the initial N args match the corresponding types */
+    public static boolean typesMatchUpTo(Object[] argsArray, Class<?>[] parameterTypes, int lengthRequired, Optional<? extends TypeCoercer> coercer) {
         if (argsArray.length < lengthRequired || parameterTypes.length < lengthRequired)
             return false;
         for (int i=0; i<lengthRequired; i++) {
-            if (argsArray[i]==null) continue;
-            if (Boxing.boxedType(parameterTypes[i]).isInstance(argsArray[i])) continue;
-            return false;
+            if (!typeMatches(argsArray[i], parameterTypes[i], coercer)) return false;
         }
         return true;
     }
 
+    /** true iff the initial N args match the corresponding types */
+    public static boolean typeMatches(Object arg, Class<?> parameterType, Optional<? extends TypeCoercer> coercer) {
+        if (parameterType.isPrimitive() && arg == null) return false;
+        if (arg == null) return true;
+        if (Boxing.boxedType(parameterType).isInstance(arg)) return true;
+        if (coercer.isPresent() && coercer.get().tryCoerce(arg, parameterType).isPresent()) return true;
+        return false;
+    }
+
     /**
      * Gets all the interfaces implemented by the given type, including its parent classes.
      *

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/7ddca12d/utils/common/src/test/java/org/apache/brooklyn/util/javalang/ReflectionsTest.java
----------------------------------------------------------------------
diff --git a/utils/common/src/test/java/org/apache/brooklyn/util/javalang/ReflectionsTest.java b/utils/common/src/test/java/org/apache/brooklyn/util/javalang/ReflectionsTest.java
index 52aee8c..ba4b1d3 100644
--- a/utils/common/src/test/java/org/apache/brooklyn/util/javalang/ReflectionsTest.java
+++ b/utils/common/src/test/java/org/apache/brooklyn/util/javalang/ReflectionsTest.java
@@ -26,10 +26,13 @@ import java.lang.reflect.Method;
 import java.util.Arrays;
 import java.util.List;
 
-import org.apache.brooklyn.util.javalang.Reflections;
+import org.apache.brooklyn.util.javalang.coerce.CommonAdaptorTypeCoercions;
+import org.apache.brooklyn.util.javalang.coerce.TypeCoercer;
+import org.apache.brooklyn.util.javalang.coerce.TypeCoercerExtensible;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
@@ -129,6 +132,22 @@ public class ReflectionsTest {
     }
     
     @Test
+    public void testInvocationCoercingArgs() throws Exception {
+        TypeCoercerExtensible rawCoercer = TypeCoercerExtensible.newDefault();
+        new CommonAdaptorTypeCoercions(rawCoercer).registerAllAdapters();
+        Optional<TypeCoercer> coercer = Optional.<TypeCoercer>of(rawCoercer);
+        
+        Method m1Short = CI1.class.getMethod("m1", String.class, int.class);
+        Method m1Long = CI1.class.getMethod("m1", String.class, int.class, int.class, int[].class);
+        Assert.assertEquals(m1Short.invoke(null, "hello", 1), "hello1");
+        Assert.assertEquals(m1Long.invoke(null, "hello", 1, 2, new int[] { 3, 4}), "hello10");
+        
+        Assert.assertEquals(Reflections.invokeMethodFromArgs(CI1.class, "m1", Arrays.<Object>asList('a', "2"), false, coercer).get(), "a2");
+        Assert.assertEquals(Reflections.invokeMethodFromArgs(CI1.class, "m1", Arrays.<Object>asList('a', "3", "4", "5"), false, coercer).get(), "a12");
+        Assert.assertEquals(Reflections.invokeMethodFromArgs(CI1.class, "m1", Arrays.<Object>asList('a', (byte)3, (byte)4, (byte)5), false, coercer).get(), "a12");
+    }
+    
+    @Test
     public void testMethodInvocation() throws Exception {
         Method m1Short = CI1.class.getMethod("m1", String.class, int.class);
         Method m1Long = CI1.class.getMethod("m1", String.class, int.class, int.class, int[].class);