You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2017/02/15 18:31:07 UTC
[14/28] brooklyn-server git commit: merge default values in config
inheritance
merge default values in config inheritance
as per (6) at https://issues.apache.org/jira/browse/BROOKLYN-329
(only does it for yaml parameters, but that's a limitation we can live with i think;
notes included on doing it for java)
also switches to new `fromString` which adjusts the interpretation of inheritance "none" to be NOT_INHERITED,
with new keyword "never" available if caller really wants NEVER_INHERITED
(this is in keeping with previous changes to the semantics)
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/a2f8b207
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/a2f8b207
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/a2f8b207
Branch: refs/heads/master
Commit: a2f8b207cc576c350a9a9e9d3431bc8c2df86e8d
Parents: cc639c5
Author: Alex Heneveld <al...@Alexs-MacBook-Pro.local>
Authored: Mon Dec 5 10:50:38 2016 +0000
Committer: Alex Heneveld <al...@Alexs-MacBook-Pro.local>
Committed: Tue Dec 6 11:33:16 2016 +0000
----------------------------------------------------------------------
.../internal/AbstractBrooklynObjectSpec.java | 29 +--
.../BrooklynComponentTemplateResolver.java | 65 ++++---
.../brooklyn/ConfigInheritanceYamlTest.java | 2 +-
.../camp/brooklyn/ConfigParametersYamlTest.java | 45 +++++
.../core/config/BasicConfigInheritance.java | 25 +++
.../brooklyn/core/objs/BasicSpecParameter.java | 184 +++++++++++++++++--
.../brooklyn/config/ConfigInheritance.java | 1 +
7 files changed, 291 insertions(+), 60 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a2f8b207/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java b/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
index dd65a44..e21d765 100644
--- a/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
+++ b/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
@@ -22,11 +22,20 @@ import static com.google.common.base.Preconditions.checkNotNull;
import java.io.Serializable;
import java.lang.reflect.Modifier;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import org.apache.brooklyn.api.mgmt.EntityManager;
+import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.api.objs.BrooklynObject;
+import org.apache.brooklyn.api.objs.SpecParameter;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
+import org.apache.brooklyn.util.collections.MutableSet;
+import org.apache.brooklyn.util.exceptions.Exceptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -37,15 +46,6 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
-import org.apache.brooklyn.api.mgmt.EntityManager;
-import org.apache.brooklyn.api.mgmt.Task;
-import org.apache.brooklyn.api.objs.BrooklynObject;
-import org.apache.brooklyn.api.objs.SpecParameter;
-import org.apache.brooklyn.config.ConfigKey;
-import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
-import org.apache.brooklyn.util.collections.MutableSet;
-import org.apache.brooklyn.util.exceptions.Exceptions;
-
/**
* Defines a spec for creating a {@link BrooklynObject}.
* <p>
@@ -147,11 +147,13 @@ public abstract class AbstractBrooklynObjectSpec<T,SpecT extends AbstractBrookly
// it is a CatalogConfig or merely a config key, maybe introducing displayable, or even priority
// (but note part of the reason for CatalogConfig.priority is that java reflection doesn't preserve field order) .
// see also comments on the camp SpecParameterResolver.
+
+ // probably the thing to do is deprecate the ambiguous method in favour of an explicit
@Beta
public SpecT parameters(List<? extends SpecParameter<?>> parameters) {
return parametersReplace(parameters);
}
- /** adds the given parameters */
+ /** adds the given parameters, new ones first so they dominate subsequent ones */
@Beta
public SpecT parametersAdd(List<? extends SpecParameter<?>> parameters) {
// parameters follows immutable pattern, unlike the other fields
@@ -159,15 +161,14 @@ public abstract class AbstractBrooklynObjectSpec<T,SpecT extends AbstractBrookly
Set<SpecParameter<?>> current = MutableSet.<SpecParameter<?>>copyOf(this.parameters);
current.removeAll(params);
- this.parameters = ImmutableList.<SpecParameter<?>>builder()
+ return parametersReplace(ImmutableList.<SpecParameter<?>>builder()
.addAll(params)
.addAll(current)
- .build();
- return self();
+ .build());
}
/** replaces parameters with the given */
@Beta
- public SpecT parametersReplace(List<? extends SpecParameter<?>> parameters) {
+ public SpecT parametersReplace(Collection<? extends SpecParameter<?>> parameters) {
this.parameters = ImmutableList.copyOf(checkNotNull(parameters, "parameters"));
return self();
}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a2f8b207/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
index 6e1bede..298196d 100644
--- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
+++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
@@ -197,7 +197,7 @@ public class BrooklynComponentTemplateResolver {
List<EntitySpecResolver> overrides = new ArrayList<>();
Iterable<ServiceTypeResolver> loader = FrameworkLookup.lookupAll(ServiceTypeResolver.class, mgmt.getCatalogClassLoader());
for (ServiceTypeResolver resolver : loader) {
- overrides.add(new ServiceTypeResolverAdaptor(this, resolver));
+ overrides.add(new ServiceTypeResolverAdaptor(this, resolver));
}
return overrides;
}
@@ -222,7 +222,9 @@ public class BrooklynComponentTemplateResolver {
for (Map<String,?> childAttrs : children) {
BrooklynComponentTemplateResolver entityResolver = BrooklynComponentTemplateResolver.Factory.newInstance(loader, childAttrs);
// encounteredRegisteredTypeIds must contain the items currently being loaded (the dependency chain),
- // but not parent items in this type already resolved.
+ // but not parent items in this type already resolved (this is because an item's definition should
+ // not include itself here, as a defined child, as that would create an endless loop;
+ // use in a member spec is fine)
EntitySpec<? extends Entity> childSpec = entityResolver.resolveSpec(encounteredRegisteredTypeIds);
spec.child(EntityManagementUtils.unwrapEntity(childSpec));
}
@@ -284,15 +286,15 @@ public class BrooklynComponentTemplateResolver {
bag.putAsStringKeyIfAbsent(r.getFlagName(), r.getFlagMaybeValue().get());
}
-
+
// now set configuration for all the items in the bag
Map<String, ConfigKey<?>> entityConfigKeys = findAllConfigKeys(spec);
-
+
Collection<FlagConfigKeyAndValueRecord> records = findAllFlagsAndConfigKeyValues(spec, bag);
Set<String> keyNamesUsed = new LinkedHashSet<String>();
for (FlagConfigKeyAndValueRecord r: records) {
// flags and config keys tracked separately, look at each (may be overkill but it's what we've always done)
-
+
Function<Maybe<Object>, Maybe<Object>> rawConvFn = Functions.identity();
if (r.getFlagMaybeValue().isPresent()) {
final String flag = r.getFlagName();
@@ -307,20 +309,20 @@ public class BrooklynComponentTemplateResolver {
}
};
Iterable<? extends ConfigValueAtContainer<EntitySpec<?>,Object>> ckvi = MutableList.of(
- new LazyContainerAndKeyValue<EntitySpec<?>,Object>(key, null, rawEvalFn, rawConvFn));
-
+ new LazyContainerAndKeyValue<EntitySpec<?>,Object>(key, null, rawEvalFn, rawConvFn));
+
ConfigValueAtContainer<EntitySpec<?>,Object> combinedVal = ConfigInheritances.resolveInheriting(
- null, key, Maybe.ofAllowingNull(ownValueF), Maybe.<Object>absent(),
- ckvi.iterator(), InheritanceContext.TYPE_DEFINITION, getDefaultConfigInheritance()).getWithoutError();
-
+ null, key, Maybe.ofAllowingNull(ownValueF), Maybe.<Object>absent(),
+ ckvi.iterator(), InheritanceContext.TYPE_DEFINITION, getDefaultConfigInheritance()).getWithoutError();
+
spec.configure(flag, combinedVal.get());
keyNamesUsed.add(flag);
}
-
+
if (r.getConfigKeyMaybeValue().isPresent()) {
final ConfigKey<Object> key = (ConfigKey<Object>) r.getConfigKey();
final Object ownValueF = new SpecialFlagsTransformer(loader, encounteredRegisteredTypeIds).apply(r.getConfigKeyMaybeValue().get());
-
+
Function<EntitySpec<?>, Maybe<Object>> rawEvalFn = new Function<EntitySpec<?>,Maybe<Object>>() {
@Override
public Maybe<Object> apply(EntitySpec<?> input) {
@@ -328,12 +330,12 @@ public class BrooklynComponentTemplateResolver {
}
};
Iterable<? extends ConfigValueAtContainer<EntitySpec<?>,Object>> ckvi = MutableList.of(
- new LazyContainerAndKeyValue<EntitySpec<?>,Object>(key, null, rawEvalFn, rawConvFn));
-
+ new LazyContainerAndKeyValue<EntitySpec<?>,Object>(key, null, rawEvalFn, rawConvFn));
+
ConfigValueAtContainer<EntitySpec<?>,Object> combinedVal = ConfigInheritances.resolveInheriting(
- null, key, Maybe.ofAllowingNull(ownValueF), Maybe.<Object>absent(),
- ckvi.iterator(), InheritanceContext.TYPE_DEFINITION, getDefaultConfigInheritance()).getWithoutError();
-
+ null, key, Maybe.ofAllowingNull(ownValueF), Maybe.<Object>absent(),
+ ckvi.iterator(), InheritanceContext.TYPE_DEFINITION, getDefaultConfigInheritance()).getWithoutError();
+
spec.configure(key, combinedVal.get());
keyNamesUsed.add(key.getName());
}
@@ -352,7 +354,7 @@ public class BrooklynComponentTemplateResolver {
spec.removeFlag(key.getName());
}
}
-
+
// set unused keys as anonymous config keys -
// they aren't flags or known config keys, so must be passed as config keys in order for
// EntitySpec to know what to do with them (as they are passed to the spec as flags)
@@ -386,14 +388,14 @@ public class BrooklynComponentTemplateResolver {
// a key in `EntitySpec.config`, then the put will replace the value and leave the key
// as-is (so the default-value and description of the key will remain as whatever the
// first put said).
-
+
// TODO We should remove duplicates, rather than just doing the `put` multiple times,
// relying on ordering. We should also respect the ordered returned by
// EntityDynamicType.getConfigKeys, which is much better (it respects `BasicConfigKeyOverwriting`
// etc).
- // Or rather if the parameter fields are incomplete they might be merged with those defined
- // on the type (eg description, default value) or ancestor, so that it isn't necessary for users
- // to re-declare those in a parameter definition, just anything they wish to overwrite.
+ // Or rather if the parameter fields are incomplete they might be merged with those defined
+ // on the type (eg description, default value) or ancestor, so that it isn't necessary for users
+ // to re-declare those in a parameter definition, just anything they wish to overwrite.
//
// However, that is hard/fiddly because of the way a config key can be referenced by
// its real name or flag-name.
@@ -406,8 +408,9 @@ public class BrooklynComponentTemplateResolver {
// name "B" the "val2" then applies to both!
//
// I plan to propose a change on dev@brooklyn, to replace `@SetFromFlag`!
-
- // need to de-dupe? (can't use Set bc FCKVR doesn't impl equals/hashcode)
+
+ // need to de-dupe? (can't use Set bc FCKVR doesn't impl equals/hashcode)
+ // TODO merge *bagFlags* with existing spec params, merge yaml w yaml parent params elsewhere
List<FlagConfigKeyAndValueRecord> allKeys = MutableList.of();
allKeys.addAll(FlagUtils.findAllParameterConfigKeys(spec.getParameters(), bagFlags));
if (spec.getImplementation() != null) {
@@ -421,13 +424,23 @@ public class BrooklynComponentTemplateResolver {
}
private Map<String, ConfigKey<?>> findAllConfigKeys(EntitySpec<?> spec) {
+ // TODO use in BasicSpecParameter to resolve ancestor config keys ?
Set<Class<?>> types = MutableSet.<Class<?>>builder()
- .add(spec.getType())
.add(spec.getImplementation())
+ .add(spec.getType())
.addAll(spec.getAdditionalInterfaces())
.remove(null)
.build();
+ // order above is important, respected below to take the first one defined
MutableMap<String, ConfigKey<?>> result = MutableMap.copyOf(FlagUtils.findAllConfigKeys(null, types));
+
+ // put parameters atop config keys
+ // TODO currently at this point parameters have been merged with ancestor spec parameters,
+ // but *not* with config keys defined on the java type
+ // see comments in BasicSpecParameter;
+ // one way to fix would be to record in BasicSpecParameter which type fields are explicitly set
+ // and to do a further merge here with result.remove(param.getConfigKey().getName());
+ // another way, probably simpler, would be to do the above result computation in BasicSpecParameter
for (SpecParameter<?> param : spec.getParameters()) {
result.put(param.getConfigKey().getName(), param.getConfigKey());
}
@@ -497,7 +510,7 @@ public class BrooklynComponentTemplateResolver {
Map<String, Object> resolvedConfig = (Map<String, Object>)transformSpecialFlags(specConfig.getSpecConfiguration());
specConfig.setSpecConfiguration(resolvedConfig);
EntitySpec<?> entitySpec = Factory.newInstance(getLoader(), specConfig.getSpecConfiguration()).resolveSpec(encounteredRegisteredTypeIds);
-
+
return EntityManagementUtils.unwrapEntity(entitySpec);
}
if (flag instanceof ManagementContextInjectable) {
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a2f8b207/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ConfigInheritanceYamlTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ConfigInheritanceYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ConfigInheritanceYamlTest.java
index bccf750..408a846 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ConfigInheritanceYamlTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ConfigInheritanceYamlTest.java
@@ -519,7 +519,7 @@ public class ConfigInheritanceYamlTest extends AbstractYamlTest {
" default: {myDefaultKey: myDefaultVal}",
" - name: map.type-never",
" type: java.util.Map",
- " inheritance.parent: none",
+ " inheritance.parent: never",
" default: {myDefaultKey: myDefaultVal}");
// Test retrieval of defaults
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a2f8b207/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ConfigParametersYamlTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ConfigParametersYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ConfigParametersYamlTest.java
index 35d3a12..b001f27 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ConfigParametersYamlTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ConfigParametersYamlTest.java
@@ -31,6 +31,7 @@ import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.api.location.PortRange;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.config.ConfigPredicates;
import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
import org.apache.brooklyn.core.location.PortRanges;
import org.apache.brooklyn.core.sensor.Sensors;
@@ -51,6 +52,7 @@ import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.google.common.base.Joiner;
+import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
@@ -576,6 +578,49 @@ public class ConfigParametersYamlTest extends AbstractYamlRebindTest {
}
@Test
+ public void testConfigParameterInSubInheritsDefaultFromYaml() throws Exception {
+ // TODO note that the corresponding functionality to inherit config info from a *java* config key is not supported
+ // see notes in BasicParameterSpec
+
+ addCatalogItems(
+ "brooklyn.catalog:",
+ " itemType: entity",
+ " items:",
+ " - id: entity-with-keys",
+ " item:",
+ " type: "+TestEntity.class.getName(),
+ " brooklyn.parameters:",
+ " - name: my.param.key",
+ " type: string",
+ " description: description one",
+ " default: myDefaultVal",
+ " brooklyn.config:",
+ " my.other.key: $brooklyn:config(\"my.param.key\")");
+
+ addCatalogItems(
+ "brooklyn.catalog:",
+ " itemType: entity",
+ " items:",
+ " - id: wrapper-entity",
+ " item:",
+ " brooklyn.parameters:",
+ " - name: my.param.key",
+ " description: description two",
+ " type: entity-with-keys");
+
+ String yaml = Joiner.on("\n").join(
+ "services:",
+ "- type: wrapper-entity");
+
+ Entity app = createStartWaitAndLogApplication(yaml);
+ final TestEntity entity = (TestEntity) Iterables.getOnlyElement(app.getChildren());
+ LOG.info("Config keys declared on "+entity+": "+entity.config().findKeysDeclared(Predicates.alwaysTrue()));
+ ConfigKey<?> key = Iterables.getOnlyElement( entity.config().findKeysDeclared(ConfigPredicates.nameEqualTo("my.param.key")) );
+ assertEquals(key.getDescription(), "description two");
+ assertEquals(entity.config().get(key), "myDefaultVal");
+ }
+
+ @Test
public void testSubTypeUsesDefaultsFromSuperInConfigMerging() throws Exception {
RecordingSshTool.setCustomResponse(".*myCommand.*", new RecordingSshTool.CustomResponse(0, "myResponse", null));
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a2f8b207/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigInheritance.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigInheritance.java b/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigInheritance.java
index d064eb9..d63d330 100644
--- a/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigInheritance.java
+++ b/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigInheritance.java
@@ -35,6 +35,7 @@ import org.apache.brooklyn.util.collections.CollectionMerger;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.exceptions.ReferenceWithError;
import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.text.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -398,4 +399,28 @@ public class BasicConfigInheritance implements ConfigInheritance {
public String toString() {
return super.toString()+"[reinherit="+isReinherited()+"; strategy="+getConflictResolutionStrategy()+"; localDefaultResolvesWithAncestor="+localDefaultResolvesWithAncestorValue+"]";
}
+
+ public static ConfigInheritance fromString(String val) {
+ if (Strings.isBlank(val)) return null;
+ // in all cases below the first value is preferred, but to preserve backwards compatibility we
+ // need to support some of the other names/strategies; yoml should give us a way to migrate to canonical form
+ switch (val.toLowerCase().trim()) {
+ case "not_reinherited":
+ case "notreinherited":
+ case "none":
+ return NOT_REINHERITED;
+ case "never":
+ return NEVER_INHERITED;
+ case "overwrite":
+ case "always":
+ return OVERWRITE;
+ case "deep_merge":
+ case "merge":
+ case "deepmerge":
+ return DEEP_MERGE;
+ default:
+ throw new IllegalArgumentException("Invalid config-inheritance '"+val+"' (legal values are none, always or deep_merge)");
+ }
+ }
+
}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a2f8b207/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java b/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java
index cc9d66a..5344bc1 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java
@@ -20,6 +20,7 @@ package org.apache.brooklyn.core.objs;
import java.lang.reflect.Field;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
@@ -40,10 +41,14 @@ import org.apache.brooklyn.api.sensor.AttributeSensor;
import org.apache.brooklyn.config.ConfigInheritance;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
+import org.apache.brooklyn.core.config.BasicConfigInheritance;
import org.apache.brooklyn.core.config.BasicConfigKey;
import org.apache.brooklyn.core.config.BasicConfigKey.Builder;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.config.ConfigKeys.InheritanceContext;
import org.apache.brooklyn.core.sensor.PortAttributeSensorAndConfigKey;
import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.util.guava.Maybe;
import org.apache.brooklyn.util.text.StringPredicates;
import org.apache.brooklyn.util.time.Duration;
@@ -51,6 +56,7 @@ import org.apache.brooklyn.util.time.Duration;
import com.google.common.annotations.Beta;
import com.google.common.base.Function;
import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
@@ -62,11 +68,11 @@ public class BasicSpecParameter<T> implements SpecParameter<T>{
private static final long serialVersionUID = -4728186276307619778L;
private final String label;
-
+
/** pinning may become a priority or other more expansive indicator */
@Beta
private final boolean pinned;
-
+
private final ConfigKey<T> configKey;
private final AttributeSensor<?> sensor;
@@ -74,15 +80,16 @@ public class BasicSpecParameter<T> implements SpecParameter<T>{
// Automatically called by xstream (which is used under the covers by XmlMementoSerializer).
// Required for those who have state from a version between
// 29th October 2015 and 21st January 2016 (when this class was introduced, and then when it was changed).
+ /** @deprecated since 0.9.0 can be removed, along with readResolve, when field not used any further */
private ConfigKey<T> type;
private Object readResolve() {
if (type != null && configKey == null) {
- return new BasicSpecParameter(label, pinned, type, sensor);
+ return new BasicSpecParameter<T>(label, pinned, type, sensor);
} else {
return this;
}
}
-
+
@Beta
public BasicSpecParameter(String label, boolean pinned, ConfigKey<T> config) {
this(label, pinned, config, null);
@@ -105,7 +112,7 @@ public class BasicSpecParameter<T> implements SpecParameter<T>{
public boolean isPinned() {
return pinned;
}
-
+
@Override
public ConfigKey<T> getConfigKey() {
return configKey;
@@ -227,13 +234,30 @@ public class BasicSpecParameter<T> implements SpecParameter<T>{
String label = (String)inputDef.get("label");
String description = (String)inputDef.get("description");
String type = (String)inputDef.get("type");
- Object defaultValue = inputDef.get("default");
Boolean pinned = (Boolean)inputDef.get("pinned");
+ boolean hasDefaultValue = inputDef.containsKey("default");
+ Object defaultValue = inputDef.get("default");
if (specialFlagTransformer != null) {
defaultValue = specialFlagTransformer.apply(defaultValue);
}
+ boolean hasConstraints = inputDef.containsKey("constraints");
Predicate<?> constraint = parseConstraints(inputDef.get("constraints"), loader);
- ConfigInheritance parentInheritance = parseInheritance(inputDef.get("inheritance.parent"), loader);
+
+ ConfigInheritance runtimeInheritance;
+ boolean hasRuntimeInheritance;
+ if (inputDef.containsKey("inheritance.runtime")) {
+ hasRuntimeInheritance = true;
+ runtimeInheritance = parseInheritance(inputDef.get("inheritance.runtime"), loader);
+ } else if (inputDef.containsKey("inheritance.parent")) {
+ // this alias will be deprecated
+ hasRuntimeInheritance = true;
+ runtimeInheritance = parseInheritance(inputDef.get("inheritance.parent"), loader);
+ } else {
+ hasRuntimeInheritance = false;
+ runtimeInheritance = parseInheritance(null, loader);
+ }
+
+ boolean hasTypeInheritance = inputDef.containsKey("inheritance.type");
ConfigInheritance typeInheritance = parseInheritance(inputDef.get("inheritance.type"), loader);
if (name == null) {
@@ -242,15 +266,17 @@ public class BasicSpecParameter<T> implements SpecParameter<T>{
ConfigKey configType;
AttributeSensor sensorType = null;
-
+
+ boolean hasType = type!=null;
+
TypeToken typeToken = inferType(type, loader);
Builder builder = BasicConfigKey.builder(typeToken)
- .name(name)
- .description(description)
- .defaultValue(defaultValue)
- .constraint(constraint)
- .runtimeInheritance(parentInheritance)
- .typeInheritance(typeInheritance);
+ .name(name)
+ .description(description)
+ .defaultValue(defaultValue)
+ .constraint(constraint)
+ .runtimeInheritance(runtimeInheritance)
+ .typeInheritance(typeInheritance);
if (PortRange.class.equals(typeToken.getRawType())) {
sensorType = new PortAttributeSensorAndConfigKey(builder);
@@ -258,7 +284,9 @@ public class BasicSpecParameter<T> implements SpecParameter<T>{
} else {
configType = builder.build();
}
- return new BasicSpecParameter(Objects.firstNonNull(label, name), !Boolean.FALSE.equals(pinned), configType, sensorType);
+
+ return new SpecParameterForInheritance(label, pinned, configType, sensorType,
+ hasType, hasDefaultValue, hasConstraints, hasRuntimeInheritance, hasTypeInheritance);
}
@SuppressWarnings({ "rawtypes" })
@@ -309,11 +337,10 @@ public class BasicSpecParameter<T> implements SpecParameter<T>{
return Predicates.alwaysTrue();
}
}
-
+
private static ConfigInheritance parseInheritance(Object obj, BrooklynClassLoadingContext loader) {
if (obj == null || obj instanceof String) {
- // TODO
- return ConfigInheritance.Legacy.fromString((String)obj);
+ return BasicConfigInheritance.fromString((String)obj);
} else {
throw new IllegalArgumentException ("The config-inheritance '" + obj + "' for a catalog input is invalid format - string supported");
}
@@ -404,7 +431,126 @@ public class BasicSpecParameter<T> implements SpecParameter<T>{
spec.parametersAdd(BasicSpecParameter.fromSpec(loader.getManagementContext(), spec));
}
if (explicitParams.size() > 0) {
- spec.parametersAdd(explicitParams);
+ spec.parametersReplace(resolveParameters(explicitParams, spec.getParameters(), spec));
+ }
+ }
+
+ /** merge parameters against other parameters and known and type-inherited config keys */
+ static Collection<SpecParameter<?>> resolveParameters(Collection<? extends SpecParameter<?>> newParams, Collection<? extends SpecParameter<?>> existingReferenceParamsToKeep, AbstractBrooklynObjectSpec<?,?> spec) {
+ Map<String,SpecParameter<?>> existingToKeep = MutableMap.of();
+ if (existingReferenceParamsToKeep!=null) {
+ for (SpecParameter<?> p: existingReferenceParamsToKeep) {
+ existingToKeep.put(p.getConfigKey().getName(), p);
+ }
+ }
+
+ List<SpecParameter<?>> result = MutableList.<SpecParameter<?>>of();
+ for (SpecParameter<?> p: newParams) {
+ if (p instanceof SpecParameterForInheritance) {
+ SpecParameter<?> existingP = existingToKeep.remove(p.getConfigKey().getName());
+ if (existingP!=null) {
+ p = ((SpecParameterForInheritance<?>)p).resolveWithAncestor(existingP);
+ } else {
+ // TODO find config keys on the type (not available as parameters)
+ /* we don't currently do this due to low priority; all it means if there is a config key in java,
+ * and a user wishes to expose it as a parameter, they have to redeclare everything;
+ * nothing from the config key in java will be inherited */
+ p = ((SpecParameterForInheritance<?>)p).resolveWithAncestor((ConfigKey<?>)null);
+ }
+ result.add(p);
+ } else {
+ existingToKeep.remove(p.getConfigKey().getName());
+ result.add(p);
+ }
+ }
+ result.addAll(existingToKeep.values());
+ return result;
+ }
+
+ /** instance of {@link SpecParameter} which includes information about what fields are explicitly set,
+ * for use with a subsequent merge */
+ @SuppressWarnings("serial")
+ @Beta
+ static class SpecParameterForInheritance<T> extends BasicSpecParameter<T> {
+
+ private final boolean hasType, hasLabelSet, hasPinnedSet, hasDefaultValue, hasConstraints, hasRuntimeInheritance, hasTypeInheritance;
+
+ private <SensorType> SpecParameterForInheritance(String label, Boolean pinned, ConfigKey<T> config, AttributeSensor<SensorType> sensor,
+ boolean hasType, boolean hasDefaultValue, boolean hasConstraints, boolean hasRuntimeInheritance, boolean hasTypeInheritance) {
+ super(Preconditions.checkNotNull(label!=null ? label : config.getName(), "label or config name must be set"),
+ pinned==null ? true : pinned, config, sensor);
+ this.hasType = hasType;
+ this.hasLabelSet = label!=null;
+ this.hasPinnedSet = pinned!=null;
+ this.hasDefaultValue = hasDefaultValue;
+ this.hasConstraints = hasConstraints;
+ this.hasRuntimeInheritance = hasRuntimeInheritance;
+ this.hasTypeInheritance = hasTypeInheritance;
+ }
+
+ /** used if yaml specifies a parameter, but possibly incomplete, and a spec supertype has a parameter */
+ SpecParameter<?> resolveWithAncestor(SpecParameter<?> ancestor) {
+ if (ancestor==null) return new BasicSpecParameter<>(getLabel(), isPinned(), getConfigKey(), getSensor());
+
+ return new BasicSpecParameter<>(
+ hasLabelSet ? getLabel() : ancestor.getLabel(),
+ hasPinnedSet ? isPinned() : ancestor.isPinned(),
+ resolveWithAncestorConfigKey(ancestor.getConfigKey()),
+ hasType ? getSensor() : ancestor.getSensor());
+ }
+
+ /** as {@link #resolveWithAncestor(SpecParameter)} but where the param redefines/extends a config key coming from a java supertype, rather than a parameter */
+ // TODO not used yet; see calls to the other resolveWithAncestor method,
+ // and see BrooklynComponentTemplateResolver.findAllConfigKeys;
+ // also note whilst it is easiest to do this here, logically it is messy,
+ // and arguably it should be done when converting the spec to an instance
+ SpecParameter<?> resolveWithAncestor(ConfigKey<?> ancestor) {
+ if (ancestor==null) return new BasicSpecParameter<>(getLabel(), isPinned(), getConfigKey(), getSensor());
+
+ // TODO probably want to do this (but it could get expensive!)
+ // Set<Class<?>> types = MutableSet.<Class<?>>builder()
+ // .add(spec.getImplementation())
+ // .add(spec.getType())
+ // .addAll(spec.getAdditionalInterfaces())
+ // .remove(null)
+ // .build();
+ // // order above is important, respected below to take the first one defined
+ // MutableMap<String, ConfigKey<?>> result = MutableMap.copyOf(FlagUtils.findAllConfigKeys(null, types));
+
+ return new BasicSpecParameter<>(
+ getLabel(),
+ isPinned(),
+ resolveWithAncestorConfigKey(ancestor),
+ // TODO port sensor will be lost (see messy code above which sets the port sensor)
+ getSensor());
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private ConfigKey<?> resolveWithAncestorConfigKey(ConfigKey<?> ancestor) {
+ ConfigKey<?> dominant = getConfigKey();
+ return ConfigKeys.<Object>builder((TypeToken)(hasType ? dominant : ancestor).getTypeToken())
+ .name(dominant.getName())
+ .description((dominant.getDescription()!=null ? dominant : ancestor).getDescription())
+ .defaultValue((hasDefaultValue ? dominant : ancestor).getDefaultValue())
+ .constraint((Predicate<Object>) (hasConstraints ? dominant : ancestor).getConstraint())
+ .runtimeInheritance( (hasRuntimeInheritance ? dominant : ancestor).getInheritanceByContext(InheritanceContext.RUNTIME_MANAGEMENT))
+ .typeInheritance( (hasTypeInheritance ? dominant : ancestor).getInheritanceByContext(InheritanceContext.TYPE_DEFINITION))
+ // not yet configurable from yaml
+ //.reconfigurable()
+ .build();
+ }
+
+ public boolean isHasDefaultValue() {
+ return hasDefaultValue;
+ }
+ public boolean isHasConstraints() {
+ return hasConstraints;
+ }
+ public boolean isHasRuntimeInheritance() {
+ return hasRuntimeInheritance;
+ }
+ public boolean isHasTypeInheritance() {
+ return hasTypeInheritance;
}
}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a2f8b207/utils/common/src/main/java/org/apache/brooklyn/config/ConfigInheritance.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/config/ConfigInheritance.java b/utils/common/src/main/java/org/apache/brooklyn/config/ConfigInheritance.java
index 203348e..f089f96 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/config/ConfigInheritance.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/config/ConfigInheritance.java
@@ -130,6 +130,7 @@ public interface ConfigInheritance extends Serializable {
/** @deprecated since 0.10.0 see implementations of this interface */ @Deprecated
public static class Legacy {
+ /** @deprecated since 0.10.0 see fromString in selected subclasses of {@link ConfigInheritance} eg BasicConfigInheritance */
public static ConfigInheritance fromString(String val) {
if (Strings.isBlank(val)) return null;
switch (val.toLowerCase().trim()) {