Improved $brooklyn:object() DSL. - Support parameterised constructors - Support static factory 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/26af0a21 Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/26af0a21 Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/26af0a21 Branch: refs/heads/master Commit: 26af0a214f4ecfd593e821bf77bac5d9b8c7d116 Parents: f3f3993 Author: Alasdair Hodge <git...@alasdairhodge.co.uk> Authored: Fri Aug 5 14:26:55 2016 +0100 Committer: Alasdair Hodge <git...@alasdairhodge.co.uk> Committed: Fri Aug 5 15:46:49 2016 +0100 ---------------------------------------------------------------------- .../spi/dsl/methods/BrooklynDslCommon.java | 133 +++++++++++++++---- .../brooklyn/camp/brooklyn/ObjectsYamlTest.java | 64 +++++++++ 2 files changed, 173 insertions(+), 24 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/26af0a21/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java index 23cc3a3..d6dfddd 100644 --- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java +++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java @@ -19,6 +19,7 @@ package org.apache.brooklyn.camp.brooklyn.spi.dsl.methods; import static com.google.common.base.Preconditions.checkNotNull; +import static org.apache.brooklyn.camp.brooklyn.spi.dsl.DslUtils.resolved; import java.util.Arrays; import java.util.Iterator; @@ -38,10 +39,8 @@ import org.apache.brooklyn.camp.brooklyn.BrooklynCampReservedKeys; import org.apache.brooklyn.camp.brooklyn.spi.creation.BrooklynYamlTypeInstantiator; import org.apache.brooklyn.camp.brooklyn.spi.creation.EntitySpecConfiguration; import org.apache.brooklyn.camp.brooklyn.spi.dsl.BrooklynDslDeferredSupplier; -import org.apache.brooklyn.camp.brooklyn.spi.dsl.DslUtils; import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent.Scope; import org.apache.brooklyn.core.config.external.ExternalConfigSupplier; -import org.apache.brooklyn.core.entity.AbstractEntity; import org.apache.brooklyn.core.entity.EntityDynamicType; import org.apache.brooklyn.core.entity.EntityInternal; import org.apache.brooklyn.core.mgmt.internal.ExternalConfigSupplierRegistry; @@ -66,6 +65,7 @@ import org.slf4j.LoggerFactory; import com.google.common.base.Function; import com.google.common.base.Objects; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; @@ -178,6 +178,9 @@ public class BrooklynDslCommon { public static Object object(Map<String, Object> arguments) { ConfigBag config = ConfigBag.newInstance(arguments); String typeName = BrooklynYamlTypeInstantiator.InstantiatorFromKey.extractTypeName("object", config).orNull(); + List<Object> constructorArgs = (List<Object>) config.getStringKeyMaybe("constructor.args").or(ImmutableList.of()); + String factoryMethodName = (String) config.getStringKeyMaybe("factoryMethod.name").orNull(); + List<Object> factoryMethodArgs = (List<Object>) config.getStringKeyMaybe("factoryMethod.args").or(ImmutableList.of()); Map<String,Object> objectFields = (Map<String, Object>) config.getStringKeyMaybe("object.fields").or(MutableMap.of()); Map<String,Object> brooklynConfig = (Map<String, Object>) config.getStringKeyMaybe(BrooklynCampReservedKeys.BROOKLYN_CONFIG).or(MutableMap.of()); @@ -187,17 +190,24 @@ public class BrooklynDslCommon { type = new ClassLoaderUtils(BrooklynDslCommon.class).loadClass(mappedTypeName); } catch (ClassNotFoundException e) { LOG.debug("Cannot load class " + typeName + " for DLS object; assuming it is in OSGi bundle; will defer its loading"); - return new DslObject(mappedTypeName, objectFields, brooklynConfig); + return new DslObject(mappedTypeName, constructorArgs, objectFields, brooklynConfig); } if (!Reflections.hasNoArgConstructor(type)) { throw new IllegalStateException(String.format("Cannot construct %s bean: No public no-arg constructor available", type)); } - if ((objectFields.isEmpty() || DslUtils.resolved(objectFields.values())) && - (brooklynConfig.isEmpty() || DslUtils.resolved(brooklynConfig.values()))) { - return DslObject.create(type, objectFields, brooklynConfig); + if (resolved(constructorArgs) && resolved(factoryMethodArgs) && resolved(objectFields.values()) && resolved(brooklynConfig.values())) { + if (factoryMethodName == null) { + return DslObject.create(type, constructorArgs, objectFields, brooklynConfig); + } else { + return DslObject.create(type, factoryMethodName, factoryMethodArgs, objectFields, brooklynConfig); + } } else { - return new DslObject(type, objectFields, brooklynConfig); + if (factoryMethodName == null) { + return new DslObject(type, constructorArgs, objectFields, brooklynConfig); + } else { + return new DslObject(type, factoryMethodName, factoryMethodArgs, objectFields, brooklynConfig); + } } } @@ -213,7 +223,7 @@ public class BrooklynDslCommon { * are not yet fully resolved. */ public static Object formatString(final String pattern, final Object...args) { - if (DslUtils.resolved(args)) { + if (resolved(args)) { // if all args are resolved, apply the format string now return String.format(pattern, args); } else { @@ -222,7 +232,7 @@ public class BrooklynDslCommon { } public static Object regexReplacement(final Object source, final Object pattern, final Object replacement) { - if (DslUtils.resolved(Arrays.asList(source, pattern, replacement))) { + if (resolved(Arrays.asList(source, pattern, replacement))) { return (new Functions.RegexReplacer(String.valueOf(pattern), String.valueOf(replacement))).apply(String.valueOf(source)); } else { return new DslRegexReplacement(source, pattern, replacement); @@ -327,18 +337,66 @@ public class BrooklynDslCommon { private static final long serialVersionUID = 8878388748085419L; - private String typeName; - private Class<?> type; - private Map<String,Object> fields, config; + private final String typeName; + private final String factoryMethodName; + private final Class<?> type; + private final List<Object> constructorArgs, factoryMethodArgs; + private final Map<String, Object> fields, config; + + public DslObject( + String typeName, + List<Object> constructorArgs, + Map<String, Object> fields, + Map<String, Object> config) { + this.typeName = checkNotNull(typeName, "typeName"); + this.type = null; + this.constructorArgs = constructorArgs; + this.factoryMethodName = null; + this.factoryMethodArgs = ImmutableList.of(); + this.fields = MutableMap.copyOf(fields); + this.config = MutableMap.copyOf(config); + } + + public DslObject( + Class<?> type, + List<Object> constructorArgs, + Map<String, Object> fields, + Map<String, Object> config) { + this.typeName = null; + this.type = checkNotNull(type, "type"); + this.constructorArgs = constructorArgs; + this.factoryMethodName = null; + this.factoryMethodArgs = ImmutableList.of(); + this.fields = MutableMap.copyOf(fields); + this.config = MutableMap.copyOf(config); + } - public DslObject(String typeName, Map<String,Object> fields, Map<String,Object> config) { + public DslObject( + String typeName, + String factoryMethodName, + List<Object> factoryMethodArgs, + Map<String, Object> fields, + Map<String, Object> config) { this.typeName = checkNotNull(typeName, "typeName"); + this.type = null; + this.constructorArgs = ImmutableList.of(); + this.factoryMethodName = factoryMethodName; + this.factoryMethodArgs = factoryMethodArgs; this.fields = MutableMap.copyOf(fields); this.config = MutableMap.copyOf(config); } - - public DslObject(Class<?> type, Map<String,Object> fields, Map<String,Object> config) { + + public DslObject( + Class<?> type, + String factoryMethodName, + List<Object> factoryMethodArgs, + Map<String, Object> fields, + Map<String, Object> config) { + this.typeName = null; this.type = checkNotNull(type, "type"); + this.constructorArgs = ImmutableList.of(); + this.factoryMethodName = factoryMethodName; + this.factoryMethodArgs = factoryMethodArgs; this.fields = MutableMap.copyOf(fields); this.config = MutableMap.copyOf(config); } @@ -346,6 +404,7 @@ public class BrooklynDslCommon { @SuppressWarnings("unchecked") @Override public Task<Object> newTask() { + Class<?> type = this.type; if (type == null) { EntityInternal entity = entity(); try { @@ -354,6 +413,8 @@ public class BrooklynDslCommon { throw Exceptions.propagate(e); } } + final Class<?> clazz = type; + List<TaskAdaptable<Object>> tasks = Lists.newLinkedList(); for (Object value : Iterables.concat(fields.values(), config.values())) { if (value instanceof TaskAdaptable) { @@ -362,8 +423,8 @@ public class BrooklynDslCommon { tasks.add(((TaskFactory<TaskAdaptable<Object>>) value).newTask()); } } - - Map<String,?> flags = MutableMap.<String,String>of("displayName", "building '"+type+"' with "+tasks.size()+" task"+(tasks.size()!=1?"s":"")); + + Map<String,?> flags = MutableMap.<String,String>of("displayName", "building '"+clazz+"' with "+tasks.size()+" task"+(tasks.size()!=1?"s":"")); return DependentConfiguration.transformMultiple(flags, new Function<List<Object>, Object>() { @Override public Object apply(List<Object> input) { @@ -384,24 +445,48 @@ public class BrooklynDslCommon { config.put(name, ((DeferredSupplier<?>) value).get()); } } - return create(type, fields, config); + if (factoryMethodName == null) { + return create(clazz, constructorArgs, fields, config); + } else { + return create(clazz, factoryMethodName, factoryMethodArgs, fields, config); + } } }, tasks); } - public static <T> T create(Class<T> type, Map<String,?> fields, Map<String,?> config) { + public static <T> T create(Class<T> type, List<?> constructorArgs, Map<String,?> fields, Map<String,?> config) { try { T bean; try { bean = (T) TypeCoercions.coerce(fields, type); } catch (ClassCoercionException ex) { - bean = Reflections.invokeConstructorFromArgs(type).get(); + bean = Reflections.invokeConstructorFromArgs(type, constructorArgs.toArray()).get(); + BeanUtils.populate(bean, fields); + } + if (bean instanceof Configurable && config.size() > 0) { + ConfigBag configBag = ConfigBag.newInstance(config); + FlagUtils.setFieldsFromFlags(bean, configBag); + FlagUtils.setAllConfigKeys((Configurable) bean, configBag, true); + } + return bean; + } catch (Exception e) { + throw Exceptions.propagate(e); + } + } + + public static Object create(Class<?> type, String factoryMethodName, List<Object> factoryMethodArgs, Map<String,?> fields, Map<String,?> config) { + try { + Object bean; + try { + bean = TypeCoercions.coerce(fields, type); + } catch (ClassCoercionException ex) { + bean = Reflections.invokeMethodFromArgs(type, factoryMethodName, factoryMethodArgs).get(); BeanUtils.populate(bean, fields); } if (bean instanceof Configurable && config.size() > 0) { - ConfigBag brooklyn = ConfigBag.newInstance(config); - FlagUtils.setFieldsFromFlags(bean, brooklyn); - FlagUtils.setAllConfigKeys((Configurable) bean, brooklyn, true); + ConfigBag configBag = ConfigBag.newInstance(config); + FlagUtils.setFieldsFromFlags(bean, configBag); + FlagUtils.setAllConfigKeys((Configurable) bean, configBag, true); } return bean; } catch (Exception e) { @@ -485,7 +570,7 @@ public class BrooklynDslCommon { public static class Functions { public static Object regexReplacement(final Object pattern, final Object replacement) { - if (DslUtils.resolved(pattern, replacement)) { + if (resolved(pattern, replacement)) { return new RegexReplacer(String.valueOf(pattern), String.valueOf(replacement)); } else { return new DslRegexReplacer(pattern, replacement); http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/26af0a21/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ObjectsYamlTest.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ObjectsYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ObjectsYamlTest.java index 147ae1e..ca4f91b 100644 --- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ObjectsYamlTest.java +++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ObjectsYamlTest.java @@ -53,8 +53,21 @@ public class ObjectsYamlTest extends AbstractYamlTest { private Integer number; private Object object; + // Factory method. + public static TestObject newTestObject(String string, Integer number, Object object) { + return new TestObject(string + "-suffix", number + 1, object); + } + + // No-arg constructor. public TestObject() { } + // Parameterised constructor. + public TestObject(String string, Integer number, Object object) { + this.string = string; + this.number = number; + this.object = object; + } + public String getString() { return string; } public void setString(String string) { this.string = string; } @@ -275,6 +288,57 @@ public class ObjectsYamlTest extends AbstractYamlTest { Assert.assertTrue(testObjectObject instanceof SimpleTestPojo, "Expected a SimpleTestPojo: "+testObjectObject); } + @Test + public void testBrooklynObjectWithParameterisedConstructor() throws Exception { + Entity testEntity = setupAndCheckTestEntityInBasicYamlWith( + " brooklyn.config:", + " test.confObject:", + " $brooklyn:object:", + " type: "+ObjectsYamlTest.class.getName()+"$TestObject", + " constructor.args:", + " - frog", + " - 7", + " - $brooklyn:object:", + " type: org.apache.brooklyn.camp.brooklyn.SimpleTestPojo" + ); + + Object testObject = testEntity.getConfig(TestEntity.CONF_OBJECT); + + Assert.assertTrue(testObject instanceof TestObject, "Expected a TestObject: "+testObject); + Assert.assertTrue(managementContextInjected.get()); + Assert.assertEquals(((TestObject) testObject).getNumber(), Integer.valueOf(7)); + Assert.assertEquals(((TestObject) testObject).getString(), "frog"); + + Object testObjectObject = ((TestObject) testObject).getObject(); + Assert.assertTrue(testObjectObject instanceof SimpleTestPojo, "Expected a SimpleTestPojo: "+testObjectObject); + } + + @Test + public void testBrooklynObjectWithFactoryMethod() throws Exception { + Entity testEntity = setupAndCheckTestEntityInBasicYamlWith( + " brooklyn.config:", + " test.confObject:", + " $brooklyn:object:", + " type: "+ObjectsYamlTest.class.getName()+"$TestObject", + " factoryMethod.name: newTestObject", + " factoryMethod.args:", + " - frog", + " - 7", + " - $brooklyn:object:", + " type: org.apache.brooklyn.camp.brooklyn.SimpleTestPojo" + ); + + Object testObject = testEntity.getConfig(TestEntity.CONF_OBJECT); + + Assert.assertTrue(testObject instanceof TestObject, "Expected a TestObject: "+testObject); + Assert.assertTrue(managementContextInjected.get()); + Assert.assertEquals(((TestObject) testObject).getNumber(), Integer.valueOf(8)); + Assert.assertEquals(((TestObject) testObject).getString(), "frog-suffix"); + + Object testObjectObject = ((TestObject) testObject).getObject(); + Assert.assertTrue(testObjectObject instanceof SimpleTestPojo, "Expected a SimpleTestPojo: "+testObjectObject); + } + @Override protected Logger getLogger() { return log;