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:41 UTC
[3/6] brooklyn-server git commit: TypeCoercions: support more generic
coercers
TypeCoercions: support more generic coercers
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/b945fdc0
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/b945fdc0
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/b945fdc0
Branch: refs/heads/master
Commit: b945fdc08c41d1c3c944e9a41663d52902426baa
Parents: 377454e
Author: Aled Sage <al...@gmail.com>
Authored: Mon May 22 14:09:54 2017 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Tue May 23 14:45:33 2017 +0100
----------------------------------------------------------------------
.../brooklyn/util/core/flags/TypeCoercions.java | 15 ++-
.../coerce/CommonAdaptorTryCoercions.java | 110 +++++++++++++++++++
.../coerce/CommonAdaptorTypeCoercions.java | 6 +-
.../util/javalang/coerce/TryCoercer.java | 45 ++++++++
.../javalang/coerce/TypeCoercerExtensible.java | 76 ++++---------
.../coerce/TypeCoercerExtensibleTest.java | 82 ++++++++++++++
6 files changed, 269 insertions(+), 65 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b945fdc0/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 9b39a56..9bc25e2 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
@@ -18,9 +18,6 @@
*/
package org.apache.brooklyn.util.core.flags;
-import groovy.lang.Closure;
-import groovy.time.TimeDuration;
-
import java.lang.reflect.Constructor;
import java.util.Map;
@@ -40,19 +37,25 @@ import org.apache.brooklyn.util.guava.Maybe;
import org.apache.brooklyn.util.javalang.Boxing;
import org.apache.brooklyn.util.javalang.JavaClassNames;
import org.apache.brooklyn.util.javalang.Reflections;
+import org.apache.brooklyn.util.javalang.coerce.CommonAdaptorTryCoercions;
import org.apache.brooklyn.util.javalang.coerce.CommonAdaptorTypeCoercions;
import org.apache.brooklyn.util.javalang.coerce.EnumTypeCoercions;
import org.apache.brooklyn.util.javalang.coerce.PrimitiveStringTypeCoercions;
+import org.apache.brooklyn.util.javalang.coerce.TryCoercer;
import org.apache.brooklyn.util.javalang.coerce.TypeCoercer;
import org.apache.brooklyn.util.javalang.coerce.TypeCoercerExtensible;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.annotations.Beta;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMap;
import com.google.common.reflect.TypeToken;
+import groovy.lang.Closure;
+import groovy.time.TimeDuration;
+
/** Static class providing a shared {@link TypeCoercer} for all of Brooklyn */
public class TypeCoercions {
@@ -68,6 +71,7 @@ public class TypeCoercions {
public static void initStandardAdapters() {
new BrooklynCommonAdaptorTypeCoercions(coercer).registerAllAdapters();
+ new CommonAdaptorTryCoercions(coercer).registerAllAdapters();
registerDeprecatedBrooklynAdapters();
registerBrooklynAdapters();
registerGroovyAdapters();
@@ -82,6 +86,11 @@ public class TypeCoercions {
return coercer.registerAdapter(sourceType, targetType, fn);
}
+ @Beta
+ public static void registerAdapter(TryCoercer fn) {
+ coercer.registerAdapter(fn);
+ }
+
public static <T> Function<Object, T> function(final Class<T> type) {
return coercer.function(type);
}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b945fdc0/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CommonAdaptorTryCoercions.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CommonAdaptorTryCoercions.java b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CommonAdaptorTryCoercions.java
new file mode 100644
index 0000000..b0a494a
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CommonAdaptorTryCoercions.java
@@ -0,0 +1,110 @@
+/*
+ * 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.util.javalang.coerce;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.List;
+
+import org.apache.brooklyn.util.exceptions.CompoundRuntimeException;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.javalang.JavaClassNames;
+import org.apache.brooklyn.util.text.Strings;
+
+import com.google.common.collect.Lists;
+import com.google.common.reflect.TypeToken;
+
+/**
+ * Defines and registers common generic coercers (e.g. to call a "fromXyz" method, to
+ * convert from one type to another).
+ */
+public class CommonAdaptorTryCoercions {
+
+ private final TypeCoercerExtensible coercer;
+
+ public CommonAdaptorTryCoercions(TypeCoercerExtensible coercer) {
+ this.coercer = coercer;
+ }
+
+ public CommonAdaptorTryCoercions registerAllAdapters() {
+ registerAdapter(new TryCoercerWithFromMethod());
+ registerAdapter(new TryCoercerToEnum());
+ registerAdapter(new TryCoercerForPrimitivesAndStrings());
+ return this;
+ }
+
+ /** Registers an adapter for use with type coercion. */
+ public synchronized void registerAdapter(TryCoercer fn) {
+ coercer.registerAdapter(fn);
+ }
+
+ protected static class TryCoercerWithFromMethod implements TryCoercer {
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> Maybe<T> tryCoerce(Object input, TypeToken<T> targetType) {
+ Class<? super T> rawTargetType = targetType.getRawType();
+
+ List<ClassCoercionException> exceptions = Lists.newArrayList();
+ //now look for static TargetType.fromType(Type t) where value instanceof Type
+ for (Method m: rawTargetType.getMethods()) {
+ if (((m.getModifiers()&Modifier.STATIC)==Modifier.STATIC) &&
+ m.getName().startsWith("from") && m.getParameterTypes().length==1 &&
+ m.getParameterTypes()[0].isInstance(input)) {
+ if (m.getName().equals("from"+JavaClassNames.verySimpleClassName(m.getParameterTypes()[0]))) {
+ try {
+ return Maybe.of((T) m.invoke(null, input));
+ } catch (Exception e) {
+ exceptions.add(new ClassCoercionException("Cannot coerce type "+input.getClass()+" to "+rawTargetType.getCanonicalName()+" ("+input+"): "+m.getName()+" adapting failed", e));
+ }
+ }
+ }
+ }
+ if (exceptions.isEmpty()) {
+ return null;
+ } else if (exceptions.size() == 1) {
+ return Maybe.absent(exceptions.get(0));
+ } else {
+ String errMsg = "Failed coercing type "+input.getClass()+" to "+rawTargetType.getCanonicalName();
+ return Maybe.absent(new CompoundRuntimeException(errMsg, exceptions));
+ }
+ }
+ }
+
+ protected static class TryCoercerToEnum implements TryCoercer {
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> Maybe<T> tryCoerce(Object input, TypeToken<T> targetType) {
+ Class<? super T> rawTargetType = targetType.getRawType();
+
+ //for enums call valueOf with the string representation of the value
+ if (rawTargetType.isEnum()) {
+ return EnumTypeCoercions.tryCoerceUntyped(Strings.toString(input), (Class<T>)rawTargetType);
+ } else {
+ return null;
+ }
+ }
+ }
+
+ protected static class TryCoercerForPrimitivesAndStrings implements TryCoercer {
+ @Override
+ public <T> Maybe<T> tryCoerce(Object input, TypeToken<T> targetType) {
+ return PrimitiveStringTypeCoercions.tryCoerce(input, targetType.getRawType());
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b945fdc0/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CommonAdaptorTypeCoercions.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CommonAdaptorTypeCoercions.java b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CommonAdaptorTypeCoercions.java
index 03745e6..b74548e 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CommonAdaptorTypeCoercions.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CommonAdaptorTypeCoercions.java
@@ -41,8 +41,8 @@ import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.net.Cidr;
import org.apache.brooklyn.util.net.Networking;
import org.apache.brooklyn.util.net.UserAndHostAndPort;
-import org.apache.brooklyn.util.text.Strings;
import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;
+import org.apache.brooklyn.util.text.Strings;
import org.apache.brooklyn.util.time.Duration;
import org.apache.brooklyn.util.time.Time;
import org.apache.brooklyn.util.yaml.Yamls;
@@ -61,10 +61,6 @@ public class CommonAdaptorTypeCoercions {
this.coercer = coercer;
}
- public TypeCoercerExtensible getCoercer() {
- return coercer;
- }
-
public CommonAdaptorTypeCoercions registerAllAdapters() {
registerStandardAdapters();
registerRecursiveIterableAdapters();
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b945fdc0/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/TryCoercer.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/TryCoercer.java b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/TryCoercer.java
new file mode 100644
index 0000000..f410fbc
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/TryCoercer.java
@@ -0,0 +1,45 @@
+/*
+ * 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.util.javalang.coerce;
+
+import org.apache.brooklyn.util.guava.Maybe;
+
+import com.google.common.annotations.Beta;
+import com.google.common.reflect.TypeToken;
+
+/**
+ * A coercer that can be registered, which will try to coerce the given input to the given type.
+ *
+ * This can be used for "generic" coercers, such as those that look for a {@code fromValue()}
+ * method on the target type.
+ */
+@Beta
+public interface TryCoercer {
+
+ /**
+ * The meaning of the return value is:
+ * <ul>
+ * <li>null - no errors, recommend continue with fallbacks (i.e. not found).
+ * <li>absent - had some kind of exception, recommend continue with fallbacks (but can report this error if
+ * other fallbacks fail).
+ * <li>present - coercion successful.
+ * </ul>
+ */
+ <T> Maybe<T> tryCoerce(Object input, TypeToken<T> type);
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b945fdc0/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/TypeCoercerExtensible.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/TypeCoercerExtensible.java b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/TypeCoercerExtensible.java
index 366ae26..a673131 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/TypeCoercerExtensible.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/TypeCoercerExtensible.java
@@ -18,8 +18,6 @@
*/
package org.apache.brooklyn.util.javalang.coerce;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
@@ -28,17 +26,15 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
-import org.apache.brooklyn.util.exceptions.CompoundRuntimeException;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.guava.Maybe;
import org.apache.brooklyn.util.javalang.Boxing;
-import org.apache.brooklyn.util.javalang.JavaClassNames;
-import org.apache.brooklyn.util.text.Strings;
import org.apache.brooklyn.util.time.Duration;
import org.apache.brooklyn.util.time.Time;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.annotations.Beta;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.collect.HashBasedTable;
@@ -77,7 +73,10 @@ public class TypeCoercerExtensible implements TypeCoercer {
/** has all the strategies (primitives, collections, etc)
* and all the adapters from {@link CommonAdaptorTypeCoercions} */
public static TypeCoercerExtensible newDefault() {
- return new CommonAdaptorTypeCoercions(newEmpty()).registerAllAdapters().getCoercer();
+ TypeCoercerExtensible result = newEmpty();
+ new CommonAdaptorTypeCoercions(result).registerAllAdapters();
+ new CommonAdaptorTryCoercions(result).registerAllAdapters();
+ return result;
}
/** has all the strategies (primitives, collections, etc) but no adapters,
@@ -87,7 +86,10 @@ public class TypeCoercerExtensible implements TypeCoercer {
}
/** Store the coercion {@link Function functions} in a {@link Table table}. */
- private Table<Class<?>, Class<?>, Function<?,?>> registry = HashBasedTable.create();
+ private final Table<Class<?>, Class<?>, Function<?,?>> registry = HashBasedTable.create();
+
+ /** Store the generic coercers. */
+ private final List<TryCoercer> genericCoercers = Lists.newCopyOnWriteArrayList();
@Override
public <T> T coerce(Object value, Class<T> targetType) {
@@ -142,13 +144,11 @@ public class TypeCoercerExtensible implements TypeCoercer {
if (targetType.isInstance(value)) return Maybe.of( (T) value );
- result = PrimitiveStringTypeCoercions.tryCoerce(value, targetType);
- if (result!=null && result.isPresent()) return result;
- if (result!=null && firstError==null) firstError = result;
-
- result = tryCoerceWithFromMethod(value, targetType);
- if (result!=null && result.isPresent()) return result;
- if (result!=null && firstError==null) firstError = result;
+ for (TryCoercer coercer : genericCoercers) {
+ result = coercer.tryCoerce(value, targetTypeToken);
+ if (result!=null && result.isPresent()) return result;
+ if (result!=null && firstError==null) firstError = result;
+ }
//ENHANCEMENT could look in type hierarchy of both types for a conversion method...
@@ -165,13 +165,6 @@ public class TypeCoercerExtensible implements TypeCoercer {
}
}
- //for enums call valueOf with the string representation of the value
- if (targetType.isEnum()) {
- result = EnumTypeCoercions.tryCoerceUntyped(Strings.toString(value), (Class<T>)targetType);
- if (result!=null && result.isPresent()) return result;
- if (result!=null && firstError==null) firstError = result;
- }
-
//now look in registry
synchronized (registry) {
Map<Class<?>, Function<?,?>> adapters = registry.row(targetType);
@@ -212,42 +205,6 @@ public class TypeCoercerExtensible implements TypeCoercer {
return Maybe.absent(new ClassCoercionException("Cannot coerce type "+value.getClass().getCanonicalName()+" to "+targetType.getCanonicalName()+" ("+value+"): no adapter known"));
}
- /**
- * The meaning of the return value is:
- * <ul>
- * <li>null - no errors, continue with fallbacks (i.e. not found).
- * <li>absent - had some kind of exception, continue with fallbacks (but can report this error if
- * other fallbacks fail).
- * <li>present - coercion successful, return value.
- * </ul>
- */
- @SuppressWarnings("unchecked")
- protected <T> Maybe<T> tryCoerceWithFromMethod(Object value, Class<? super T> targetType) {
- List<ClassCoercionException> exceptions = Lists.newArrayList();
- //now look for static TargetType.fromType(Type t) where value instanceof Type
- for (Method m: targetType.getMethods()) {
- if (((m.getModifiers()&Modifier.STATIC)==Modifier.STATIC) &&
- m.getName().startsWith("from") && m.getParameterTypes().length==1 &&
- m.getParameterTypes()[0].isInstance(value)) {
- if (m.getName().equals("from"+JavaClassNames.verySimpleClassName(m.getParameterTypes()[0]))) {
- try {
- return Maybe.of((T) m.invoke(null, value));
- } catch (Exception e) {
- exceptions.add(new ClassCoercionException("Cannot coerce type "+value.getClass()+" to "+targetType.getCanonicalName()+" ("+value+"): "+m.getName()+" adapting failed", e));
- }
- }
- }
- }
- if (exceptions.isEmpty()) {
- return null;
- } else if (exceptions.size() == 1) {
- return Maybe.absent(exceptions.get(0));
- } else {
- String errMsg = "Failed coercing type "+value.getClass()+" to "+targetType.getCanonicalName();
- return Maybe.absent(new CompoundRuntimeException(errMsg, exceptions));
- }
- }
-
@SuppressWarnings("unchecked")
protected <T> Maybe<T> tryCoerceMap(Object value, TypeToken<T> targetTypeToken) {
if (!(value instanceof Map) || !(Map.class.isAssignableFrom(targetTypeToken.getRawType()))) return null;
@@ -324,4 +281,9 @@ public class TypeCoercerExtensible implements TypeCoercer {
return (Function<? super A,B>) registry.put(targetType, sourceType, fn);
}
+ /** Registers a generic adapter for use with type coercion. */
+ @Beta
+ public synchronized void registerAdapter(TryCoercer fn) {
+ genericCoercers.add(fn);
+ }
}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b945fdc0/utils/common/src/test/java/org/apache/brooklyn/util/javalang/coerce/TypeCoercerExtensibleTest.java
----------------------------------------------------------------------
diff --git a/utils/common/src/test/java/org/apache/brooklyn/util/javalang/coerce/TypeCoercerExtensibleTest.java b/utils/common/src/test/java/org/apache/brooklyn/util/javalang/coerce/TypeCoercerExtensibleTest.java
new file mode 100644
index 0000000..afb10f9
--- /dev/null
+++ b/utils/common/src/test/java/org/apache/brooklyn/util/javalang/coerce/TypeCoercerExtensibleTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.util.javalang.coerce;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Objects;
+
+import org.apache.brooklyn.util.guava.Maybe;
+import org.testng.annotations.Test;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.reflect.TypeToken;
+
+public class TypeCoercerExtensibleTest {
+
+ TypeCoercerExtensible coercer = TypeCoercerExtensible.newDefault();
+
+ protected <T> T coerce(Object x, Class<T> type) {
+ return coercer.coerce(x, type);
+ }
+ protected <T> T coerce(Object x, TypeToken<T> type) {
+ return coercer.coerce(x, type);
+ }
+
+ @Test
+ public void testRegisterNewGenericCoercer() {
+ coercer.registerAdapter(new TryCoercer() {
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> Maybe<T> tryCoerce(Object input, TypeToken<T> type) {
+ if (input instanceof String && type.getRawType() == MyClazz.class) {
+ return (Maybe<T>) Maybe.of(new MyClazz("myprefix"+input));
+ } else {
+ return null;
+ }
+ }
+ });
+
+ assertEquals(coerce("abc", MyClazz.class), new MyClazz("myprefixabc"));
+ }
+
+ public static class MyClazz {
+ private final String val;
+
+ public MyClazz(String val) {
+ this.val = val;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(val);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof MyClazz)) return false;
+ return Objects.equals(val, ((MyClazz)obj).val);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this).add("val", val).toString();
+ }
+ }
+}