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:39 UTC

[1/6] brooklyn-server git commit: Test jclouds TemplateOptions coercions

Repository: brooklyn-server
Updated Branches:
  refs/heads/master 377454ed8 -> 6287271b8


Test jclouds TemplateOptions coercions


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

Branch: refs/heads/master
Commit: 610c720f73476b6a63770916f72cc9d8da75c334
Parents: 1b23cb6
Author: Aled Sage <al...@gmail.com>
Authored: Mon May 22 14:08:34 2017 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Tue May 23 14:45:33 2017 +0100

----------------------------------------------------------------------
 .../JcloudsTemplateOptionsYamlTest.java         | 118 +++++++++++++++++++
 .../JcloudsTemplateOptionsStubbedTest.java      | 102 ++++++++++++++++
 2 files changed, 220 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/610c720f/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/JcloudsTemplateOptionsYamlTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/JcloudsTemplateOptionsYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/JcloudsTemplateOptionsYamlTest.java
new file mode 100644
index 0000000..943266b
--- /dev/null
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/JcloudsTemplateOptionsYamlTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.camp.brooklyn;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.NoSuchElementException;
+
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.camp.brooklyn.JcloudsCustomizerInstantiationYamlDslTest.RecordingLocationCustomizer;
+import org.apache.brooklyn.camp.brooklyn.spi.creation.CampTypePlanTransformer;
+import org.apache.brooklyn.core.entity.trait.Startable;
+import org.apache.brooklyn.core.typereg.RegisteredTypeLoadingContexts;
+import org.apache.brooklyn.entity.machine.MachineEntity;
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.googlecomputeengine.compute.options.GoogleComputeEngineTemplateOptions;
+import org.jclouds.googlecomputeengine.domain.Instance.ServiceAccount;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+/**
+ * Tests that jcouds TemplateOptions are constructed properly from yaml blueprints.
+ */
+@Test
+public class JcloudsTemplateOptionsYamlTest extends AbstractJcloudsStubYamlTest {
+
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        RecordingLocationCustomizer.clear();
+        super.setUp();
+    }
+
+    @AfterMethod(alwaysRun=true)
+    @Override
+    public void tearDown() throws Exception {
+        try {
+            super.tearDown();
+        } finally {
+            RecordingLocationCustomizer.clear();
+        }
+    }
+    
+    @Override
+    protected String getLocationSpec() {
+        return "jclouds:google-compute-engine:us-central1-a";
+    }
+    
+    @Test
+    public void testCoercesStronglyTypedTemplateOption() throws Exception {
+        String yaml = Joiner.on("\n").join(
+                "location: " + LOCATION_CATALOG_ID,
+                "services:\n" +
+                "- type: " + MachineEntity.class.getName(),
+                "  brooklyn.config:",
+                "    onbox.base.dir.skipResolution: true",
+                "    sshMonitoring.enabled: false",
+                "    metrics.usage.retrieve: false",
+                "    enabled: true",
+                "    provisioning.properties:",
+                "      templateOptions:",
+                "        serviceAccounts:",
+                "        - email: myemail",
+                "          scopes:",
+                "          - myscope1",
+                "          - myscope2",
+                "      customizer:",
+                "        $brooklyn:object:",
+                "          type: " + RecordingLocationCustomizer.class.getName(),
+                "          object.fields:",
+                "            enabled: $brooklyn:config(\"enabled\")");
+
+        EntitySpec<?> spec = managementContext.getTypeRegistry().createSpecFromPlan(CampTypePlanTransformer.FORMAT, yaml, RegisteredTypeLoadingContexts.spec(Application.class), EntitySpec.class);
+        Entity app = managementContext.getEntityManager().createEntity(spec);
+
+        app.invoke(Startable.START, ImmutableMap.<String, Object>of()).get();
+
+        GoogleComputeEngineTemplateOptions options = (GoogleComputeEngineTemplateOptions) findTemplateOptionsInCustomizerArgs();
+        assertEquals(options.serviceAccounts(), ImmutableList.of(
+                ServiceAccount.create("myemail", ImmutableList.of("myscope1", "myscope2"))));
+    }
+    
+    private TemplateOptions findTemplateOptionsInCustomizerArgs() {
+        for (RecordingLocationCustomizer.CallParams call : RecordingLocationCustomizer.calls) {
+            Optional<?> templateOptions = Iterables.tryFind(call.args, Predicates.instanceOf(TemplateOptions.class));
+            if (templateOptions.isPresent()) {
+                return (TemplateOptions) templateOptions.get();
+            }
+        }
+        throw new NoSuchElementException();
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/610c720f/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsTemplateOptionsStubbedTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsTemplateOptionsStubbedTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsTemplateOptionsStubbedTest.java
new file mode 100644
index 0000000..a65b759
--- /dev/null
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsTemplateOptionsStubbedTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.testng.Assert.assertEquals;
+
+import org.apache.brooklyn.location.jclouds.StubbedComputeServiceRegistry.AbstractNodeCreator;
+import org.apache.brooklyn.location.jclouds.StubbedComputeServiceRegistry.NodeCreator;
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.NodeMetadata.Status;
+import org.jclouds.compute.domain.NodeMetadataBuilder;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.googlecomputeengine.compute.options.GoogleComputeEngineTemplateOptions;
+import org.jclouds.googlecomputeengine.domain.Instance.ServiceAccount;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class JcloudsTemplateOptionsStubbedTest extends AbstractJcloudsStubbedUnitTest {
+
+    @SuppressWarnings("unused")
+    private static final Logger log = LoggerFactory.getLogger(JcloudsImageChoiceStubbedLiveTest.class);
+    
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        initNodeCreatorAndJcloudsLocation(newNodeCreator(), ImmutableMap.of());
+    }
+    
+    /**
+     * For overriding.
+     */
+    protected String getLocationSpec() {
+        return "jclouds:google-compute-engine:us-central1-a";
+    }
+    
+
+    @Override
+    protected NodeCreator newNodeCreator() {
+        return new AbstractNodeCreator() {
+            @Override protected NodeMetadata newNode(String group, Template template) {
+                NodeMetadata result = new NodeMetadataBuilder()
+                        .id("myid")
+                        .credentials(LoginCredentials.builder().identity("myuser").credential("mypassword").build())
+                        .loginPort(22)
+                        .status(Status.RUNNING)
+                        .publicAddresses(ImmutableList.of("173.194.32.123"))
+                        .privateAddresses(ImmutableList.of("172.168.10.11"))
+                        .build();
+                return result;
+            }
+        };
+    }
+
+    @Test
+    public void testTemplateOption() throws Exception {
+        ServiceAccount serviceAccount = ServiceAccount.create("myemail", ImmutableList.of("myscope1"));
+        
+        RecordingLocationCustomizer customizer = new RecordingLocationCustomizer();
+        obtainMachine(ImmutableMap.builder()
+                .put(JcloudsLocation.JCLOUDS_LOCATION_CUSTOMIZERS, ImmutableList.of(customizer))
+                .put(JcloudsLocation.TEMPLATE_OPTIONS, ImmutableMap.of(
+                        "serviceAccounts", ImmutableList.of(serviceAccount)))
+                .build());
+        
+        GoogleComputeEngineTemplateOptions options = (GoogleComputeEngineTemplateOptions) customizer.templateOptions;
+        assertEquals(options.serviceAccounts(), ImmutableList.of(serviceAccount));
+    }
+
+    private static class RecordingLocationCustomizer extends BasicJcloudsLocationCustomizer {
+        TemplateOptions templateOptions;
+        
+        @Override
+        public void customize(JcloudsLocation location, ComputeService computeService, TemplateOptions templateOptions) {
+            this.templateOptions = templateOptions;
+        }
+    }
+}


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

Posted by sv...@apache.org.
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();
+        }
+    }
+}


[6/6] brooklyn-server git commit: Closes #693

Posted by sv...@apache.org.
Closes #693

jclouds TemplateOptions type coercions


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

Branch: refs/heads/master
Commit: 6287271b8a5e7f32756049e7443b6933c82142c6
Parents: 377454e 610c720
Author: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Authored: Tue May 23 17:23:25 2017 +0300
Committer: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Committed: Tue May 23 17:23:25 2017 +0300

----------------------------------------------------------------------
 .../brooklyn/AbstractJcloudsStubYamlTest.java   |   9 +-
 .../JcloudsTemplateOptionsYamlTest.java         | 118 ++++++++
 .../util/core/flags/MethodCoercions.java        |  84 ++++--
 .../brooklyn/util/core/flags/TypeCoercions.java |  15 +-
 .../util/core/flags/MethodCoercionsTest.java    |  83 ++++--
 .../location/jclouds/JcloudsLocation.java       |   4 +-
 .../jclouds/JcloudsLocationResolver.java        |   6 +-
 .../location/jclouds/JcloudsTypeCoercions.java  | 168 +++++++++++
 .../jclouds/AbstractJcloudsStubbedUnitTest.java |  27 +-
 .../jclouds/JcloudsStubTemplateBuilder.java     | 204 ++++++++++----
 .../JcloudsTemplateOptionsStubbedTest.java      | 102 +++++++
 .../JcloudsTypeCoercionsWithBuilderTest.java    | 250 +++++++++++++++++
 .../JcloudsTypeCoercionsWithCreateTest.java     | 278 +++++++++++++++++++
 .../coerce/CommonAdaptorTryCoercions.java       | 110 ++++++++
 .../coerce/CommonAdaptorTypeCoercions.java      |   6 +-
 .../util/javalang/coerce/TryCoercer.java        |  45 +++
 .../javalang/coerce/TypeCoercerExtensible.java  |  76 ++---
 .../coerce/TypeCoercerExtensibleTest.java       |  82 ++++++
 18 files changed, 1503 insertions(+), 164 deletions(-)
----------------------------------------------------------------------



[3/6] brooklyn-server git commit: TypeCoercions: support more generic coercers

Posted by sv...@apache.org.
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();
+        }
+    }
+}


[5/6] brooklyn-server git commit: jclouds stubbing: support different providers

Posted by sv...@apache.org.
jclouds stubbing: support different providers


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

Branch: refs/heads/master
Commit: 57ded1822c2cecfb0be0b55a7dc528cd9d7300d9
Parents: 8a832de
Author: Aled Sage <al...@gmail.com>
Authored: Mon May 22 14:06:31 2017 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Tue May 23 14:45:33 2017 +0100

----------------------------------------------------------------------
 .../brooklyn/AbstractJcloudsStubYamlTest.java   |   9 +-
 .../jclouds/AbstractJcloudsStubbedUnitTest.java |  27 ++-
 .../jclouds/JcloudsStubTemplateBuilder.java     | 204 ++++++++++++++-----
 3 files changed, 185 insertions(+), 55 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/57ded182/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractJcloudsStubYamlTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractJcloudsStubYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractJcloudsStubYamlTest.java
index 380c132..4854160 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractJcloudsStubYamlTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractJcloudsStubYamlTest.java
@@ -105,13 +105,17 @@ public abstract class AbstractJcloudsStubYamlTest extends AbstractJcloudsStubbed
     }
 
     protected void addStubbedLocationToCatalog() {
+        String locationSpec = getLocationSpec();
+        String provider = getProvider();
+        String regionOrEndpoint = getRegion();
+        
         addCatalogItems(
                 "brooklyn.catalog:",
                 "  id: " + LOCATION_CATALOG_ID,
                 "  version: 1.0.0",
                 "  itemType: location",
                 "  item:",
-                "    type: " + LOCATION_SPEC,
+                "    type: " + locationSpec,
                 "    brooklyn.config:",
                 "      identity: myidentity",
                 "      credential: mycredential",
@@ -124,6 +128,9 @@ public abstract class AbstractJcloudsStubYamlTest extends AbstractJcloudsStubbed
                 "        $brooklyn:object:",
                 "          type: "+JcloudsStubTemplateBuilder.class.getName(),
                 "          factoryMethod.name: create",
+                "          factoryMethod.args:",
+                "          - " + provider,
+                "          - " + regionOrEndpoint,
                 "      " + JcloudsLocation.POLL_FOR_FIRST_REACHABLE_ADDRESS_PREDICATE.getName() + ":",
                 "        $brooklyn:object:",
                 "          type: "+Predicates.class.getName(),

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/57ded182/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsStubbedUnitTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsStubbedUnitTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsStubbedUnitTest.java
index 37231be..83c6b7e 100644
--- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsStubbedUnitTest.java
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsStubbedUnitTest.java
@@ -18,8 +18,13 @@
  */
 package org.apache.brooklyn.location.jclouds;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import java.util.Map;
 
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
 import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.location.jclouds.StubbedComputeServiceRegistry.BasicNodeCreator;
@@ -33,7 +38,6 @@ import org.slf4j.LoggerFactory;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 
-import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableMap;
 
@@ -94,7 +98,7 @@ public abstract class AbstractJcloudsStubbedUnitTest extends AbstractJcloudsLive
         this.computeServiceRegistry = new StubbedComputeServiceRegistry(nodeCreator, false);
         final Map<Object, Object> defaults = ImmutableMap.builder()
                 .put(JcloudsLocationConfig.COMPUTE_SERVICE_REGISTRY, computeServiceRegistry)
-                .put(JcloudsLocationConfig.TEMPLATE_BUILDER, JcloudsStubTemplateBuilder.create())
+                .put(JcloudsLocationConfig.TEMPLATE_BUILDER, JcloudsStubTemplateBuilder.create(getProvider(), getRegion()))
                 .put(JcloudsLocationConfig.ACCESS_IDENTITY, "stub-identity")
                 .put(JcloudsLocationConfig.ACCESS_CREDENTIAL, "stub-credential")
                 .put(SshMachineLocation.SSH_TOOL_CLASS, RecordingSshTool.class.getName())
@@ -125,4 +129,23 @@ public abstract class AbstractJcloudsStubbedUnitTest extends AbstractJcloudsLive
     protected NodeCreator newNodeCreator() {
         return new BasicNodeCreator();
     }
+    
+    protected String getProvider() {
+        LocationSpec<?> spec = mgmt().getLocationRegistry().getLocationSpec(getLocationSpec()).get();
+        return getRequiredConfig(spec, JcloudsLocation.CLOUD_PROVIDER);
+    }
+    
+    protected String getRegion() {
+        LocationSpec<? extends Location> spec = mgmt().getLocationRegistry().getLocationSpec(getLocationSpec()).get();
+        return getRequiredConfig(spec, JcloudsLocation.CLOUD_REGION_ID);
+    }
+    
+    protected String getRequiredConfig(LocationSpec<?> spec, ConfigKey<String> key) {
+        String result = (String) spec.getConfig().get(key);
+        if (result != null) {
+            return result;
+        }
+        result = (String) spec.getFlags().get(key.getName());
+        return checkNotNull(result, "config "+key.getName());
+    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/57ded182/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsStubTemplateBuilder.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsStubTemplateBuilder.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsStubTemplateBuilder.java
index 0ac5fb2..e12f5cb 100644
--- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsStubTemplateBuilder.java
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsStubTemplateBuilder.java
@@ -18,16 +18,25 @@
  */
 package org.apache.brooklyn.location.jclouds;
 
-import com.google.common.base.Functions;
-import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
-import com.google.common.base.Objects.ToStringHelper;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import org.jclouds.compute.domain.*;
+import static org.jclouds.ec2.compute.domain.EC2HardwareBuilder.t2_micro;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.inject.Provider;
+
+import org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.HardwareBuilder;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.ImageBuilder;
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.compute.domain.Processor;
+import org.jclouds.compute.domain.TemplateBuilder;
+import org.jclouds.compute.domain.internal.TemplateBuilderImpl;
 import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.compute.strategy.GetImageStrategy;
 import org.jclouds.compute.suppliers.ImageCacheSupplier;
@@ -38,47 +47,57 @@ import org.jclouds.domain.LoginCredentials;
 import org.jclouds.ec2.compute.domain.RegionAndName;
 import org.jclouds.ec2.compute.functions.ImagesToRegionAndIdMap;
 import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl;
+import org.jclouds.ec2.compute.options.EC2TemplateOptions;
 import org.jclouds.ec2.domain.RootDeviceType;
 import org.jclouds.ec2.domain.VirtualizationType;
+import org.jclouds.googlecomputeengine.compute.options.GoogleComputeEngineTemplateOptions;
 import org.jclouds.rest.AuthorizationException;
+import org.jclouds.softlayer.compute.options.SoftLayerTemplateOptions;
 
-import javax.inject.Provider;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicReference;
-
-import static org.jclouds.ec2.compute.domain.EC2HardwareBuilder.*;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
+import com.google.common.base.Functions;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Predicates;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 
 public class JcloudsStubTemplateBuilder {
-    protected final Location provider = new LocationBuilder().scope(LocationScope.PROVIDER).id("aws-ec2").description("aws-ec2").build();
-    protected final Location jcloudsDomainLocation = new LocationBuilder().scope(LocationScope.REGION).id("us-east-1").description("us-east-1")
-            .parent(provider).build();
-
-    protected final Hardware HARDWARE_SUPPORTING_BOGUS = t2_micro().id("supporting-bogus")
-            .supportsImageIds(ImmutableSet.of("us-east-1/bogus-image"))
-            .virtualizationType(VirtualizationType.PARAVIRTUAL)
-            .rootDeviceType(RootDeviceType.EBS)
-            .build();
+    protected final Location provider;
+    protected final Location jcloudsDomainLocation;
 
+    protected final String providerName;
+    protected final String regionName;
+    
     public static TemplateBuilder create() {
         return new JcloudsStubTemplateBuilder().createTemplateBuilder();
     }
 
+    public static TemplateBuilder create(String providerName, String regionName) {
+        return new JcloudsStubTemplateBuilder(providerName, regionName).createTemplateBuilder();
+    }
+
+    public JcloudsStubTemplateBuilder() {
+        this("aws-ec2", "us-east-1");
+    }
+
+    public JcloudsStubTemplateBuilder(String providerName, String regionName) {
+        this.providerName = providerName;
+        this.regionName = regionName;
+        this.provider = new LocationBuilder().scope(LocationScope.PROVIDER).id(providerName).description(providerName).build();
+        this.jcloudsDomainLocation = new LocationBuilder().scope(LocationScope.REGION).id(this.regionName).description(this.regionName)
+                .parent(provider).build();
+    }
+
     public TemplateBuilder createTemplateBuilder() {
-        final Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.of(
-                new ImageBuilder().providerId("ebs-image-provider").name("image")
-                        .id("us-east-1/bogus-image").location(jcloudsDomainLocation)
-                        .userMetadata(ImmutableMap.of("rootDeviceType", RootDeviceType.EBS.value()))
-                        .operatingSystem(new OperatingSystem(OsFamily.UBUNTU, null, "1.0", VirtualizationType.PARAVIRTUAL.value(), "ubuntu", true))
-                        .description("description").version("1.0").defaultCredentials(LoginCredentials.builder().user("root").build())
-                        .status(Image.Status.AVAILABLE)
-                        .build()));
+        final Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.of(getImage()));
         ImmutableMap<RegionAndName, Image> imageMap = (ImmutableMap<RegionAndName, Image>) ImagesToRegionAndIdMap.imagesToMap(images.get());
         Supplier<LoadingCache<RegionAndName, ? extends Image>> imageCache = Suppliers.<LoadingCache<RegionAndName, ? extends Image>> ofInstance(
                 CacheBuilder.newBuilder().<RegionAndName, Image>build(CacheLoader.from(Functions.forMap(imageMap))));
-        JcloudsStubTemplateBuilder jcloudsStubTemplateBuilder = new JcloudsStubTemplateBuilder();
-        return jcloudsStubTemplateBuilder.newTemplateBuilder(images, imageCache);
+        return newTemplateBuilder(images, imageCache);
     }
 
     /**
@@ -87,10 +106,9 @@ public class JcloudsStubTemplateBuilder {
      */
     @SuppressWarnings("unchecked")
     protected TemplateBuilder newTemplateBuilder(Supplier<Set<? extends Image>> images, Supplier<LoadingCache<RegionAndName, ? extends Image>> imageCache) {
-
         Provider<TemplateOptions> optionsProvider = mock(Provider.class);
         Provider<TemplateBuilder> templateBuilderProvider = mock(Provider.class);
-        TemplateOptions defaultOptions = mock(TemplateOptions.class);
+        TemplateOptions defaultOptions = newTemplateOptions();
         final GetImageStrategy getImageStrategy = mock(GetImageStrategy.class);
 
         when(optionsProvider.get()).thenReturn(defaultOptions);
@@ -98,7 +116,7 @@ public class JcloudsStubTemplateBuilder {
         Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
                 .of(jcloudsDomainLocation));
         Supplier<Set<? extends Hardware>> sizes = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
-                .of(HARDWARE_SUPPORTING_BOGUS));
+                .of(getHardware()));
 
 
 //        AtomicReference<AuthorizationException> authException = new AtomicReference<AuthorizationException>(new AuthorizationException());
@@ -111,18 +129,100 @@ public class JcloudsStubTemplateBuilder {
             }
         };
 
-        return new EC2TemplateBuilderImpl(
-                locations,
-                new ImageCacheSupplier(images, 60, authException, imageLoader),
-                sizes,
-                Suppliers.ofInstance(jcloudsDomainLocation),
-                optionsProvider,
-                templateBuilderProvider,
-                imageCache) {
-            @Override
-            protected ToStringHelper string() {
-                return super.string().add("type", "Stubbed-TemplateBuilder");
-            }
-        };
+        switch (providerName) {
+        case "aws-ec2" :
+        case "ec2" :
+            return new EC2TemplateBuilderImpl(
+                    locations,
+                    new ImageCacheSupplier(images, 60, authException, imageLoader),
+                    sizes,
+                    Suppliers.ofInstance(jcloudsDomainLocation),
+                    optionsProvider,
+                    templateBuilderProvider,
+                    imageCache) {
+                @Override
+                protected ToStringHelper string() {
+                    return super.string().add("type", "Stubbed-TemplateBuilder");
+                }
+            };
+        default:
+            return new TemplateBuilderImpl(
+                    locations,
+                    new ImageCacheSupplier(images, 60, authException, imageLoader),
+                    sizes,
+                    Suppliers.ofInstance(jcloudsDomainLocation),
+                    optionsProvider,
+                    templateBuilderProvider) {
+                @Override
+                protected ToStringHelper string() {
+                    return super.string().add("type", "Stubbed-TemplateBuilder");
+                }
+            };
+        }
+    }
+    
+    protected TemplateOptions newTemplateOptions() {
+        switch (providerName) {
+        case "aws-ec2" :
+            return new AWSEC2TemplateOptions();
+        case "ec2" :
+            return new EC2TemplateOptions();
+        case "google-compute-engine" :
+            return new GoogleComputeEngineTemplateOptions();
+            //return mock(GoogleComputeEngineTemplateOptions.class);
+        case "azurecompute" :
+            return new org.jclouds.azurecompute.compute.options.AzureComputeTemplateOptions();
+        case "azurecompute-arm" :
+            return new org.jclouds.azurecompute.arm.compute.options.AzureTemplateOptions();
+        case "softlayer" :
+            return new SoftLayerTemplateOptions();
+        default:
+            throw new UnsupportedOperationException("Unsupported stubbed TemplateOptions for provider "+providerName);
+        }
+    }
+    
+    protected Image getImage() {
+        switch (providerName) {
+        case "aws-ec2" :
+        case "ec2" :
+            return new ImageBuilder().providerId("ebs-image-provider").name("image")
+                    .id(regionName+"/bogus-image").location(jcloudsDomainLocation)
+                    .userMetadata(ImmutableMap.of("rootDeviceType", RootDeviceType.EBS.value()))
+                    .operatingSystem(new OperatingSystem(OsFamily.UBUNTU, null, "1.0", VirtualizationType.PARAVIRTUAL.value(), "ubuntu", true))
+                    .description("description").version("1.0").defaultCredentials(LoginCredentials.builder().user("root").build())
+                    .status(Image.Status.AVAILABLE)
+                    .build();
+        case "google-compute-engine" :
+            return new ImageBuilder().providerId("gce-image-provider").name("image")
+                    .id(regionName+"/bogus-image").location(jcloudsDomainLocation)
+                    .operatingSystem(new OperatingSystem(OsFamily.UBUNTU, null, "1.0", VirtualizationType.PARAVIRTUAL.value(), "ubuntu", true))
+                    .description("description").version("1.0").defaultCredentials(LoginCredentials.builder().user("root").build())
+                    .status(Image.Status.AVAILABLE)
+                    .build();
+        default:
+            throw new UnsupportedOperationException("Unsupported stubbed Image for provider "+providerName);
+        }
+    }
+    
+    protected Hardware getHardware() {
+        switch (providerName) {
+        case "aws-ec2" :
+        case "ec2" :
+            return t2_micro().id("supporting-bogus")
+                    .supportsImageIds(ImmutableSet.of("us-east-1/bogus-image"))
+                    .virtualizationType(VirtualizationType.PARAVIRTUAL)
+                    .rootDeviceType(RootDeviceType.EBS)
+                    .build();
+        case "google-compute-engine" :
+            return new HardwareBuilder()
+                    .providerId(providerName)
+                    .id("n1-standard-1")
+                    .ram(3750)
+                    .processor(new Processor(1, 1234))
+                    .supportsImage(Predicates.alwaysTrue())
+                    .build();
+        default:
+            throw new UnsupportedOperationException("Unsupported stubbed Hardware for provider "+providerName);
+        }
     }
 }


[4/6] brooklyn-server git commit: MethodCoercions: add more utility methods

Posted by sv...@apache.org.
MethodCoercions: add more utility methods


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

Branch: refs/heads/master
Commit: 8a832de59c513d81c4e8c97e723fc03f0aadf26a
Parents: b945fdc
Author: Aled Sage <al...@gmail.com>
Authored: Mon May 22 18:09:15 2017 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Tue May 23 14:45:33 2017 +0100

----------------------------------------------------------------------
 .../util/core/flags/MethodCoercions.java        | 84 +++++++++++++++-----
 .../util/core/flags/MethodCoercionsTest.java    | 83 ++++++++++++++-----
 2 files changed, 125 insertions(+), 42 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8a832de5/core/src/main/java/org/apache/brooklyn/util/core/flags/MethodCoercions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/flags/MethodCoercions.java b/core/src/main/java/org/apache/brooklyn/util/core/flags/MethodCoercions.java
index a243581..b0afcdd 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/flags/MethodCoercions.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/flags/MethodCoercions.java
@@ -18,15 +18,7 @@
  */
 package org.apache.brooklyn.util.core.flags;
 
-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 org.apache.brooklyn.util.exceptions.Exceptions;
-import org.apache.brooklyn.util.guava.Maybe;
+import static com.google.common.base.Preconditions.checkNotNull;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -34,7 +26,16 @@ import java.lang.reflect.Type;
 import java.util.Arrays;
 import java.util.List;
 
-import static com.google.common.base.Preconditions.checkNotNull;
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.guava.Maybe;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+import com.google.common.reflect.TypeToken;
 
 /**
  * A way of binding a loosely-specified method call into a strongly-typed Java method call.
@@ -93,7 +94,7 @@ public class MethodCoercions {
             return Maybe.absent();
         }
     }
-
+    
     /**
      * Returns a predicate that matches a method with the given name, and parameters that
      * {@link org.apache.brooklyn.util.core.flags.TypeCoercions#tryCoerce(Object, com.google.common.reflect.TypeToken)} can process
@@ -104,21 +105,48 @@ public class MethodCoercions {
      * @return a predicate that will match a compatible method
      */
     public static Predicate<Method> matchMultiParameterMethod(final String methodName, final List<?> arguments) {
+        return Predicates.and(matchMethodByName(methodName), matchMultiParameterMethod(arguments));
+    }
+
+    /**
+     * Returns a predicate that matches a method with the given name.
+     *
+     * @param methodName name of the method
+     * @return a predicate that will match a compatible method
+     */
+    public static Predicate<Method> matchMethodByName(final String methodName) {
         checkNotNull(methodName, "methodName");
+
+        return new Predicate<Method>() {
+            @Override
+            public boolean apply(@Nullable Method input) {
+                return (input != null) && input.getName().equals(methodName);
+            }
+        };
+    }
+
+    /**
+     * Returns a predicate that matches a method with parameters that
+     * {@link org.apache.brooklyn.util.core.flags.TypeCoercions#tryCoerce(Object, com.google.common.reflect.TypeToken)} can process
+     * from the given list of arguments.
+     *
+     * @param arguments arguments that is intended to be given
+     * @return a predicate that will match a compatible method
+     */
+    public static Predicate<Method> matchMultiParameterMethod(final List<?> arguments) {
         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;
+                            TypeToken.of(parameterTypes[paramCount])).isPresent()) return false;
                 }
                 return true;
             }
@@ -129,15 +157,13 @@ public class MethodCoercions {
      * 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 instanceOrClazz the object or class to invoke the method on
+     * @param methods the methods to choose from
      * @param argument a list of the arguments to the method's parameters.
      * @return the result of the method call, or {@link org.apache.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));
+    public static Maybe<?> tryFindAndInvokeMultiParameterMethod(Object instanceOrClazz, Iterable<Method> methods, List<?> arguments) {
+        Optional<Method> matchingMethod = Iterables.tryFind(methods, matchMultiParameterMethod(arguments));
         if (matchingMethod.isPresent()) {
             Method method = matchingMethod.get();
             try {
@@ -148,7 +174,7 @@ public class MethodCoercions {
                     Type paramType = method.getGenericParameterTypes()[paramCount];
                     coercedArguments[paramCount] = TypeCoercions.coerce(argument, TypeToken.of(paramType));
                 }
-                return Maybe.of(method.invoke(instance, coercedArguments));
+                return Maybe.of(method.invoke(instanceOrClazz, coercedArguments));
             } catch (IllegalAccessException | InvocationTargetException e) {
                 throw Exceptions.propagate(e);
             }
@@ -156,6 +182,21 @@ public class MethodCoercions {
             return Maybe.absent();
         }
     }
+    
+    /**
+     * 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 org.apache.brooklyn.util.guava.Maybe#absent()} if method could not be matched.
+     */
+    public static Maybe<?> tryFindAndInvokeMultiParameterMethod(Object instance, String methodName, List<?> arguments) {
+        Class<?> clazz = instance.getClass();
+        Iterable<Method> methods = Iterables.filter(Arrays.asList(clazz.getMethods()), matchMethodByName(methodName));
+        return tryFindAndInvokeMultiParameterMethod(instance, methods, arguments);
+    }
 
     /**
      * Tries to find a method with each parameter compatible with (can be coerced to) the corresponding argument, and invokes it.
@@ -165,7 +206,7 @@ public class MethodCoercions {
      * @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 org.apache.brooklyn.util.guava.Maybe#absent()} if method could not be matched.
      */
-    public static Maybe<?> tryFindAndInvokeBestMatchingMethod(final Object instance, final String methodName, final Object argument) {
+    public static Maybe<?> tryFindAndInvokeBestMatchingMethod(Object instance, String methodName, Object argument) {
         if (argument instanceof List) {
             List<?> arguments = (List<?>) argument;
 
@@ -181,5 +222,4 @@ public class MethodCoercions {
             return tryFindAndInvokeSingleParameterMethod(instance, methodName, argument);
         }
     }
-
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8a832de5/core/src/test/java/org/apache/brooklyn/util/core/flags/MethodCoercionsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/util/core/flags/MethodCoercionsTest.java b/core/src/test/java/org/apache/brooklyn/util/core/flags/MethodCoercionsTest.java
index e48d2b6..57adb54 100644
--- a/core/src/test/java/org/apache/brooklyn/util/core/flags/MethodCoercionsTest.java
+++ b/core/src/test/java/org/apache/brooklyn/util/core/flags/MethodCoercionsTest.java
@@ -18,22 +18,23 @@
  */
 package org.apache.brooklyn.util.core.flags;
 
-import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableList;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import java.lang.reflect.Method;
+import java.util.List;
 
-import org.apache.brooklyn.util.core.flags.MethodCoercions;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
-import java.lang.reflect.Method;
-import java.util.List;
-
-import static org.testng.Assert.*;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
 
 public class MethodCoercionsTest {
 
+    private Method staticMultiParameterMethod;
     private Method singleParameterMethod;
     private Method multiParameterMethod;
     private Method singleCollectionParameterMethod;
@@ -41,6 +42,7 @@ public class MethodCoercionsTest {
     @BeforeClass
     public void testFixtureSetUp() {
         try {
+            staticMultiParameterMethod = TestClass.class.getMethod("staticMultiParameterMethod", boolean.class, int.class);
             singleParameterMethod = TestClass.class.getMethod("singleParameterMethod", int.class);
             multiParameterMethod = TestClass.class.getMethod("multiParameterMethod", boolean.class, int.class);
             singleCollectionParameterMethod = TestClass.class.getMethod("singleCollectionParameterMethod", List.class);
@@ -58,11 +60,19 @@ public class MethodCoercionsTest {
     }
 
     @Test
-    public void testTryFindAndInvokeSingleParameterMethod() throws Exception {
-        TestClass instance = new TestClass();
-        Maybe<?> maybe = MethodCoercions.tryFindAndInvokeSingleParameterMethod(instance, "singleParameterMethod", "42");
-        assertTrue(maybe.isPresent());
-        assertTrue(instance.wasSingleParameterMethodCalled());
+    public void testMatchSingleCollectionParameterMethod() throws Exception {
+        Predicate<Method> predicate = MethodCoercions.matchSingleParameterMethod("singleCollectionParameterMethod", ImmutableList.of("42"));
+        assertFalse(predicate.apply(singleParameterMethod));
+        assertFalse(predicate.apply(multiParameterMethod));
+        assertTrue(predicate.apply(singleCollectionParameterMethod));
+    }
+
+    @Test
+    public void testMatchMethodByName() throws Exception {
+        Predicate<Method> predicate = MethodCoercions.matchMethodByName("singleParameterMethod");
+        assertTrue(predicate.apply(singleParameterMethod));
+        assertFalse(predicate.apply(multiParameterMethod));
+        assertFalse(predicate.apply(singleCollectionParameterMethod));
     }
 
     @Test
@@ -74,6 +84,14 @@ public class MethodCoercionsTest {
     }
 
     @Test
+    public void testMatchMultiParameterMethodWithoutName() throws Exception {
+        Predicate<Method> predicate = MethodCoercions.matchMultiParameterMethod(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"));
@@ -82,6 +100,17 @@ public class MethodCoercionsTest {
     }
 
     @Test
+    public void testTryFindAndInvokeStaticMultiParameterMethod() throws Exception {
+        try {
+            Maybe<?> maybe = MethodCoercions.tryFindAndInvokeMultiParameterMethod(TestClass.class, ImmutableList.of(staticMultiParameterMethod), ImmutableList.of("true", "42"));
+            assertTrue(maybe.isPresent());
+            assertTrue(TestClass.wasStaticMultiParameterMethodCalled());
+        } finally {
+            TestClass.clear();
+        }
+    }
+
+    @Test
     public void testTryFindAndInvokeBestMatchingMethod() throws Exception {
         TestClass instance = new TestClass();
         Maybe<?> maybe = MethodCoercions.tryFindAndInvokeBestMatchingMethod(instance, "singleParameterMethod", "42");
@@ -98,29 +127,43 @@ public class MethodCoercionsTest {
         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));
+    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 testTryFindAndInvokeSingleCollectionParameterMethod() throws Exception {
         TestClass instance = new TestClass();
-        Maybe<?> maybe = MethodCoercions.tryFindAndInvokeSingleCollectionParameterMethod(instance, "singleCollectionParameterMethod", ImmutableList.of("42"));
+        Maybe<?> maybe = MethodCoercions.tryFindAndInvokeSingleParameterMethod(instance, "singleCollectionParameterMethod", ImmutableList.of("42"));
         assertTrue(maybe.isPresent());
         assertTrue(instance.wasSingleCollectionParameterMethodCalled());
     }
-*/
+
     public static class TestClass {
 
+        private static boolean staticMultiParameterMethodCalled;
+        
         private boolean singleParameterMethodCalled;
         private boolean multiParameterMethodCalled;
         private boolean singleCollectionParameterMethodCalled;
 
+        public static void staticMultiParameterMethod(boolean parameter1, int parameter2) {
+            staticMultiParameterMethodCalled = true;
+        }
+
+        public static boolean wasStaticMultiParameterMethodCalled() {
+            return staticMultiParameterMethodCalled;
+        }
+
+        public static void clear() {
+            staticMultiParameterMethodCalled = false;
+        }
+        
         public void singleParameterMethod(int parameter) {
             singleParameterMethodCalled = true;
         }