You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2016/02/01 18:50:17 UTC
[20/50] [abbrv] brooklyn-server git commit: Clarify operation of
`templateOptions` config key
Clarify operation of `templateOptions` config key
Resolves the ambiguity around single parameters of type list. Extracts
the bulk of the code into a new standalone class and adds tests. Adds
documentation.
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/6023536e
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/6023536e
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/6023536e
Branch: refs/heads/0.7.0-incubating
Commit: 6023536eb9c9fda438dc3fea42d1262a1510ae19
Parents: e26c9e3
Author: Richard Downer <ri...@apache.org>
Authored: Wed Jun 24 10:58:11 2015 +0100
Committer: Richard Downer <ri...@apache.org>
Committed: Wed Jun 24 11:03:48 2015 +0100
----------------------------------------------------------------------
.../brooklyn/util/flags/MethodCoercions.java | 183 +++++++++++++++++++
.../util/flags/MethodCoercionsTest.java | 146 +++++++++++++++
.../location/jclouds/JcloudsLocation.java | 43 +----
3 files changed, 333 insertions(+), 39 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6023536e/core/src/main/java/brooklyn/util/flags/MethodCoercions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/flags/MethodCoercions.java b/core/src/main/java/brooklyn/util/flags/MethodCoercions.java
new file mode 100644
index 0000000..c9f00fe
--- /dev/null
+++ b/core/src/main/java/brooklyn/util/flags/MethodCoercions.java
@@ -0,0 +1,183 @@
+/*
+ * 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 brooklyn.util.flags;
+
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.guava.Maybe;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.reflect.TypeToken;
+
+import javax.annotation.Nullable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * A way of binding a loosely-specified method call into a strongly-typed Java method call.
+ */
+public class MethodCoercions {
+
+ /**
+ * Returns a predicate that matches a method with the given name, and a single parameter that
+ * {@link brooklyn.util.flags.TypeCoercions#tryCoerce(Object, com.google.common.reflect.TypeToken)} can process
+ * from the given argument.
+ *
+ * @param methodName name of the method
+ * @param argument argument that is intended to be given
+ * @return a predicate that will match a compatible method
+ */
+ public static Predicate<Method> matchSingleParameterMethod(final String methodName, final Object argument) {
+ checkNotNull(methodName, "methodName");
+ checkNotNull(argument, "argument");
+
+ return new Predicate<Method>() {
+ @Override
+ public boolean apply(@Nullable Method input) {
+ if (input == null) return false;
+ if (!input.getName().equals(methodName)) return false;
+ Type[] parameterTypes = input.getGenericParameterTypes();
+ return parameterTypes.length == 1
+ && TypeCoercions.tryCoerce(argument, TypeToken.of(parameterTypes[0])).isPresentAndNonNull();
+
+ }
+ };
+ }
+
+ /**
+ * Tries to find a single-parameter method with a parameter compatible with (can be coerced to) the argument, and
+ * invokes it.
+ *
+ * @param instance the object to invoke the method on
+ * @param methodName the name of the method to invoke
+ * @param argument the argument to the method's parameter.
+ * @return the result of the method call, or {@link brooklyn.util.guava.Maybe#absent()} if method could not be matched.
+ */
+ public static Maybe<?> tryFindAndInvokeSingleParameterMethod(final Object instance, final String methodName, final Object argument) {
+ Class<?> clazz = instance.getClass();
+ Iterable<Method> methods = Arrays.asList(clazz.getMethods());
+ Optional<Method> matchingMethod = Iterables.tryFind(methods, matchSingleParameterMethod(methodName, argument));
+ if (matchingMethod.isPresent()) {
+ Method method = matchingMethod.get();
+ try {
+ Type paramType = method.getGenericParameterTypes()[0];
+ Object coercedArgument = TypeCoercions.coerce(argument, TypeToken.of(paramType));
+ return Maybe.of(method.invoke(instance, coercedArgument));
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw Exceptions.propagate(e);
+ }
+ } else {
+ return Maybe.absent();
+ }
+ }
+
+ /**
+ * Returns a predicate that matches a method with the given name, and parameters that
+ * {@link brooklyn.util.flags.TypeCoercions#tryCoerce(Object, com.google.common.reflect.TypeToken)} can process
+ * from the given list of arguments.
+ *
+ * @param methodName name of the method
+ * @param arguments arguments that is intended to be given
+ * @return a predicate that will match a compatible method
+ */
+ public static Predicate<Method> matchMultiParameterMethod(final String methodName, final List<?> arguments) {
+ checkNotNull(methodName, "methodName");
+ checkNotNull(arguments, "arguments");
+
+ return new Predicate<Method>() {
+ @Override
+ public boolean apply(@Nullable Method input) {
+ if (input == null) return false;
+ if (!input.getName().equals(methodName)) return false;
+ int numOptionParams = arguments.size();
+ Type[] parameterTypes = input.getGenericParameterTypes();
+ if (parameterTypes.length != numOptionParams) return false;
+
+ for (int paramCount = 0; paramCount < numOptionParams; paramCount++) {
+ if (!TypeCoercions.tryCoerce(((List) arguments).get(paramCount),
+ TypeToken.of(parameterTypes[paramCount])).isPresentAndNonNull()) return false;
+ }
+ return true;
+ }
+ };
+ }
+
+ /**
+ * Tries to find a multiple-parameter method with each parameter compatible with (can be coerced to) the
+ * corresponding argument, and invokes it.
+ *
+ * @param instance the object to invoke the method on
+ * @param methodName the name of the method to invoke
+ * @param argument a list of the arguments to the method's parameters.
+ * @return the result of the method call, or {@link brooklyn.util.guava.Maybe#absent()} if method could not be matched.
+ */
+ public static Maybe<?> tryFindAndInvokeMultiParameterMethod(final Object instance, final String methodName, final List<?> arguments) {
+ Class<?> clazz = instance.getClass();
+ Iterable<Method> methods = Arrays.asList(clazz.getMethods());
+ Optional<Method> matchingMethod = Iterables.tryFind(methods, matchMultiParameterMethod(methodName, arguments));
+ if (matchingMethod.isPresent()) {
+ Method method = matchingMethod.get();
+ try {
+ int numOptionParams = ((List)arguments).size();
+ Object[] coercedArguments = new Object[numOptionParams];
+ for (int paramCount = 0; paramCount < numOptionParams; paramCount++) {
+ Object argument = arguments.get(paramCount);
+ Type paramType = method.getGenericParameterTypes()[paramCount];
+ coercedArguments[paramCount] = TypeCoercions.coerce(argument, TypeToken.of(paramType));
+ }
+ return Maybe.of(method.invoke(instance, coercedArguments));
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw Exceptions.propagate(e);
+ }
+ } else {
+ return Maybe.absent();
+ }
+ }
+
+ /**
+ * Tries to find a method with each parameter compatible with (can be coerced to) the corresponding argument, and invokes it.
+ *
+ * @param instance the object to invoke the method on
+ * @param methodName the name of the method to invoke
+ * @param argument a list of the arguments to the method's parameters, or a single argument for a single-parameter method.
+ * @return the result of the method call, or {@link brooklyn.util.guava.Maybe#absent()} if method could not be matched.
+ */
+ public static Maybe<?> tryFindAndInvokeBestMatchingMethod(final Object instance, final String methodName, final Object argument) {
+ if (argument instanceof List) {
+ List<?> arguments = (List<?>) argument;
+
+ // ambiguous case: we can't tell if the user is using the multi-parameter syntax, or the single-parameter
+ // syntax for a method which takes a List parameter. So we try one, then fall back to the other.
+
+ Maybe<?> maybe = tryFindAndInvokeMultiParameterMethod(instance, methodName, arguments);
+ if (maybe.isAbsent())
+ maybe = tryFindAndInvokeSingleParameterMethod(instance, methodName, argument);
+
+ return maybe;
+ } else {
+ return tryFindAndInvokeSingleParameterMethod(instance, methodName, argument);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6023536e/core/src/test/java/brooklyn/util/flags/MethodCoercionsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/flags/MethodCoercionsTest.java b/core/src/test/java/brooklyn/util/flags/MethodCoercionsTest.java
new file mode 100644
index 0000000..4d06ca4
--- /dev/null
+++ b/core/src/test/java/brooklyn/util/flags/MethodCoercionsTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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 brooklyn.util.flags;
+
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.guava.Maybe;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+import static org.testng.Assert.*;
+
+public class MethodCoercionsTest {
+
+ private Method singleParameterMethod;
+ private Method multiParameterMethod;
+ private Method singleCollectionParameterMethod;
+
+ @BeforeClass
+ public void testFixtureSetUp() {
+ try {
+ singleParameterMethod = TestClass.class.getMethod("singleParameterMethod", int.class);
+ multiParameterMethod = TestClass.class.getMethod("multiParameterMethod", boolean.class, int.class);
+ singleCollectionParameterMethod = TestClass.class.getMethod("singleCollectionParameterMethod", List.class);
+ } catch (NoSuchMethodException e) {
+ throw Exceptions.propagate(e);
+ }
+ }
+
+ @Test
+ public void testMatchSingleParameterMethod() throws Exception {
+ Predicate<Method> predicate = MethodCoercions.matchSingleParameterMethod("singleParameterMethod", "42");
+ assertTrue(predicate.apply(singleParameterMethod));
+ assertFalse(predicate.apply(multiParameterMethod));
+ assertFalse(predicate.apply(singleCollectionParameterMethod));
+ }
+
+ @Test
+ public void testTryFindAndInvokeSingleParameterMethod() throws Exception {
+ TestClass instance = new TestClass();
+ Maybe<?> maybe = MethodCoercions.tryFindAndInvokeSingleParameterMethod(instance, "singleParameterMethod", "42");
+ assertTrue(maybe.isPresent());
+ assertTrue(instance.wasSingleParameterMethodCalled());
+ }
+
+ @Test
+ public void testMatchMultiParameterMethod() throws Exception {
+ Predicate<Method> predicate = MethodCoercions.matchMultiParameterMethod("multiParameterMethod", ImmutableList.of("true", "42"));
+ assertFalse(predicate.apply(singleParameterMethod));
+ assertTrue(predicate.apply(multiParameterMethod));
+ assertFalse(predicate.apply(singleCollectionParameterMethod));
+ }
+
+ @Test
+ public void testTryFindAndInvokeMultiParameterMethod() throws Exception {
+ TestClass instance = new TestClass();
+ Maybe<?> maybe = MethodCoercions.tryFindAndInvokeMultiParameterMethod(instance, "multiParameterMethod", ImmutableList.of("true", "42"));
+ assertTrue(maybe.isPresent());
+ assertTrue(instance.wasMultiParameterMethodCalled());
+ }
+
+ @Test
+ public void testTryFindAndInvokeBestMatchingMethod() throws Exception {
+ TestClass instance = new TestClass();
+ Maybe<?> maybe = MethodCoercions.tryFindAndInvokeBestMatchingMethod(instance, "singleParameterMethod", "42");
+ assertTrue(maybe.isPresent());
+ assertTrue(instance.wasSingleParameterMethodCalled());
+
+ instance = new TestClass();
+ maybe = MethodCoercions.tryFindAndInvokeBestMatchingMethod(instance, "multiParameterMethod", ImmutableList.of("true", "42"));
+ assertTrue(maybe.isPresent());
+ assertTrue(instance.wasMultiParameterMethodCalled());
+
+ instance = new TestClass();
+ maybe = MethodCoercions.tryFindAndInvokeBestMatchingMethod(instance, "singleCollectionParameterMethod", ImmutableList.of("fred", "joe"));
+ assertTrue(maybe.isPresent());
+ assertTrue(instance.wasSingleCollectionParameterMethodCalled());
+ }
+/*
+ @Test
+ public void testMatchSingleCollectionParameterMethod() throws Exception {
+ Predicate<Method> predicate = MethodCoercions.matchSingleCollectionParameterMethod("singleCollectionParameterMethod", ImmutableList.of("42"));
+ assertFalse(predicate.apply(singleParameterMethod));
+ assertFalse(predicate.apply(multiParameterMethod));
+ assertTrue(predicate.apply(singleCollectionParameterMethod));
+ }
+
+ @Test
+ public void testTryFindAndInvokeSingleCollectionParameterMethod() throws Exception {
+ TestClass instance = new TestClass();
+ Maybe<?> maybe = MethodCoercions.tryFindAndInvokeSingleCollectionParameterMethod(instance, "singleCollectionParameterMethod", ImmutableList.of("42"));
+ assertTrue(maybe.isPresent());
+ assertTrue(instance.wasSingleCollectionParameterMethodCalled());
+ }
+*/
+ public static class TestClass {
+
+ private boolean singleParameterMethodCalled;
+ private boolean multiParameterMethodCalled;
+ private boolean singleCollectionParameterMethodCalled;
+
+ public void singleParameterMethod(int parameter) {
+ singleParameterMethodCalled = true;
+ }
+
+ public void multiParameterMethod(boolean parameter1, int parameter2) {
+ multiParameterMethodCalled = true;
+ }
+
+ public void singleCollectionParameterMethod(List<String> parameter) {
+ singleCollectionParameterMethodCalled = true;
+ }
+
+ public boolean wasSingleParameterMethodCalled() {
+ return singleParameterMethodCalled;
+ }
+
+ public boolean wasMultiParameterMethodCalled() {
+ return multiParameterMethodCalled;
+ }
+
+ public boolean wasSingleCollectionParameterMethodCalled() {
+ return singleCollectionParameterMethodCalled;
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6023536e/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
index 7797138..02b7dde 100644
--- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
+++ b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
@@ -25,6 +25,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.jclouds.compute.options.RunScriptOptions.Builder.overrideLoginCredentials;
import static org.jclouds.scriptbuilder.domain.Statements.exec;
+
+import brooklyn.util.flags.MethodCoercions;
import io.cloudsoft.winrm4j.pywinrm.Session;
import io.cloudsoft.winrm4j.pywinrm.WinRMFactory;
@@ -1292,45 +1294,8 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im
Class<? extends TemplateOptions> clazz = options.getClass();
Iterable<Method> methods = Arrays.asList(clazz.getMethods());
for(final Map.Entry<String, Object> option : optionsMap.entrySet()) {
- Optional<Method> methodOptional = Iterables.tryFind(methods, new Predicate<Method>() {
- @Override
- public boolean apply(@Nullable Method input) {
- // Matches a method with the expected name, and a single parameter that TypeCoercions
- // can coerce to
- if (input == null) return false;
- if (!input.getName().equals(option.getKey())) return false;
- int numOptionParams = option.getValue() instanceof List ? ((List)option.getValue()).size() : 1;
- Type[] parameterTypes = input.getGenericParameterTypes();
- if (parameterTypes.length != numOptionParams) return false;
- if (numOptionParams == 1 && !(option.getValue() instanceof List) && parameterTypes.length == 1) {
- return true;
- }
- for (int paramCount = 0; paramCount < numOptionParams; paramCount ++) {
- if (!TypeCoercions.tryCoerce(((List)option.getValue()).get(paramCount),
- TypeToken.of(parameterTypes[paramCount])).isPresentAndNonNull()) return false;
- }
- return true;
- }
- });
- if(methodOptional.isPresent()) {
- try {
- Method method = methodOptional.get();
- if (option.getValue() instanceof List) {
- List<Object> parameters = Lists.newArrayList();
- int numOptionParams = ((List)option.getValue()).size();
- for (int paramCount = 0; paramCount < numOptionParams; paramCount++) {
- parameters.add(TypeCoercions.coerce(((List)option.getValue()).get(paramCount), TypeToken.of(method.getGenericParameterTypes()[paramCount])));
- }
- method.invoke(options, parameters.toArray());
- } else {
- method.invoke(options, TypeCoercions.coerce(option.getValue(), TypeToken.of(method.getGenericParameterTypes()[0])));
- }
- } catch (IllegalAccessException e) {
- throw Exceptions.propagate(e);
- } catch (InvocationTargetException e) {
- throw Exceptions.propagate(e);
- }
- } else {
+ Maybe<?> result = MethodCoercions.tryFindAndInvokeBestMatchingMethod(options, option.getKey(), option.getValue());
+ if(result.isAbsent()) {
LOG.warn("Ignoring request to set template option {} because this is not supported by {}", new Object[] { option.getKey(), clazz.getCanonicalName() });
}
}