Skip to content

Commit

Permalink
This closes #982
Browse files Browse the repository at this point in the history
  • Loading branch information
aledsage committed Sep 4, 2018
2 parents 029c7e8 + bae72fc commit 002be99
Show file tree
Hide file tree
Showing 49 changed files with 875 additions and 330 deletions.
Expand Up @@ -22,6 +22,8 @@

import javax.management.MBeanParameterInfo;

import com.google.common.reflect.TypeToken;

/**
* Similar to the concepts in the JMX {@link MBeanParameterInfo} class.
*
Expand All @@ -34,6 +36,7 @@ public interface ParameterType<T> extends Serializable {
public String getName();

public Class<T> getParameterClass();
public TypeToken<T> getParameterType();

/**
* The canonical name of the parameter class; especially useful if the class
Expand Down
Expand Up @@ -185,7 +185,7 @@ protected void findMethod() {
protected Maybe<?> resolve(Object object, boolean immediate) {
return Tasks.resolving(object, Object.class)
.context(entity().getExecutionContext())
.deep(true)
.deep(true, true)
.immediately(immediate)
.iterator()
.nextOrLast(DslFunctionSource.class);
Expand Down
Expand Up @@ -598,7 +598,7 @@ public Maybe<Object> getImmediately() {

final Function<Object, Object> resolver = new Function<Object, Object>() {
@Override public Object apply(Object value) {
Maybe<Object> result = Tasks.resolving(value, Object.class).context(executionContext).deep(true).immediately(true).getMaybe();
Maybe<Object> result = Tasks.resolving(value, Object.class).context(executionContext).deep(true, true).immediately(true).getMaybe();
if (result.isAbsent()) {
throw new ImmediateValueNotAvailableException();
} else {
Expand Down
Expand Up @@ -677,7 +677,7 @@ protected Maybe<Sensor<?>> getImmediately(Object si, boolean resolved) {
if (!resolved) {
// attempt to resolve, and recurse
final ExecutionContext executionContext = entity().getExecutionContext();
Maybe<Object> resolvedSi = Tasks.resolving(si, Object.class).deep(true).immediately(true).context(executionContext).getMaybe();
Maybe<Object> resolvedSi = Tasks.resolving(si, Object.class).deep(true, true).immediately(true).context(executionContext).getMaybe();
if (resolvedSi.isAbsent()) return Maybe.absent();
return getImmediately(resolvedSi.get(), true);
}
Expand Down Expand Up @@ -780,7 +780,7 @@ private String resolveTemplate(boolean immediately) {
.as(Object.class)
.context(findExecutionContext(this))
.immediately(immediately)
.deep(true)
.deep(true, true)
.description("Resolving substitutions " + substitutions + " for template " + template)
.get();
}
Expand Down
Expand Up @@ -57,6 +57,7 @@
import com.google.common.base.Functions;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.reflect.TypeToken;
import com.google.common.util.concurrent.ListenableScheduledFuture;
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
Expand Down Expand Up @@ -96,14 +97,14 @@ public void tearDown() throws Exception {
@Test
public void testAttributeWhenReadyEmptyDoesNotBlock() throws Exception {
BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.attributeWhenReady(TestApplication.MY_ATTRIBUTE.getName());
Maybe<?> actualValue = execDslRealRealQuick(dsl, TestApplication.MY_ATTRIBUTE.getType(), app);
Maybe<?> actualValue = execDslRealRealQuick(dsl, TestApplication.MY_ATTRIBUTE.getTypeToken(), app);
assertTrue(actualValue.isAbsent());
}

@Test
public void testAttributeWhenReadyEmptyImmediatelyDoesNotBlock() throws Exception {
BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.attributeWhenReady(TestApplication.MY_ATTRIBUTE.getName());
Maybe<?> actualValue = execDslImmediately(dsl, TestApplication.MY_ATTRIBUTE.getType(), app, true);
Maybe<?> actualValue = execDslImmediately(dsl, TestApplication.MY_ATTRIBUTE.getTypeToken(), app, true);
assertTrue(actualValue.isAbsent());
}

Expand Down Expand Up @@ -211,7 +212,7 @@ public void testConfigImmediatelyDoesNotBlock() throws Exception {
BrooklynDslDeferredSupplier<?> attributeDsl = BrooklynDslCommon.attributeWhenReady(TestApplication.MY_ATTRIBUTE.getName());
app.config().set((ConfigKey)configKey, attributeDsl); // ugly cast because val is DSL, resolving to a string
BrooklynDslDeferredSupplier<?> configDsl = BrooklynDslCommon.config(configKey.getName());
Maybe<?> actualValue = execDslImmediately(configDsl, configKey.getType(), app, true);
Maybe<?> actualValue = execDslImmediately(configDsl, configKey.getTypeToken(), app, true);
assertTrue(actualValue.isAbsent());
}

Expand Down Expand Up @@ -338,13 +339,13 @@ protected void runConcurrentWorker(Supplier<Runnable> taskSupplier) {
private static class DslTestWorker implements Runnable {
protected final TestApplication parent;
protected final BrooklynDslDeferredSupplier<?> dsl;
protected final Class<?> type;
protected final TypeToken<?> type;
protected EntitySpec<? extends TestEntity> childSpec = EntitySpec.create(TestEntity.class);
protected int resolverIterations = MANY_RESOLVER_ITERATIONS;
protected boolean satisfiedAsynchronously = false;
private boolean wrapInTaskForImmediately = true;

public DslTestWorker(TestApplication parent, BrooklynDslDeferredSupplier<?> dsl, Class<?> type) {
public DslTestWorker(TestApplication parent, BrooklynDslDeferredSupplier<?> dsl, TypeToken<?> type) {
this.parent = checkNotNull(parent, "parent");
this.dsl = checkNotNull(dsl, "dsl");
this.type = checkNotNull(type, "type");
Expand Down Expand Up @@ -409,7 +410,7 @@ private class AttributeWhenReadyTestWorker extends DslTestWorker {
private ListenableScheduledFuture<?> future;

public AttributeWhenReadyTestWorker(TestApplication parent, AttributeSensor<String> sensor, BrooklynDslDeferredSupplier<?> dsl) {
super(parent, dsl, sensor.getType());
super(parent, dsl, sensor.getTypeToken());
this.sensor = sensor;
}

Expand Down Expand Up @@ -449,7 +450,7 @@ protected void postResolve(TestEntity entity, Maybe<?> actualValue, boolean isIm

private static class SelfTestWorker extends DslTestWorker {
public SelfTestWorker(TestApplication parent, BrooklynDslDeferredSupplier<?> dsl) {
super(parent, dsl, Entity.class);
super(parent, dsl, TypeToken.of(Entity.class));
}

@Override
Expand All @@ -465,7 +466,7 @@ protected void postResolve(TestEntity entity, Maybe<?> actualValue, boolean isIm

private static class ParentTestWorker extends DslTestWorker {
public ParentTestWorker(TestApplication parent, BrooklynDslDeferredSupplier<?> dsl) {
super(parent, dsl, Entity.class);
super(parent, dsl, TypeToken.of(Entity.class));
}

@Override
Expand All @@ -488,7 +489,7 @@ public ConfigTestWorker(TestApplication parent, ConfigKey<?> config, Supplier<Co
}

public ConfigTestWorker(TestApplication parent, ConfigKey<?> config, Function<? super Entity, ConfigValuePair> valueFunction, BrooklynDslDeferredSupplier<?> dsl) {
super(parent, dsl, config.getType());
super(parent, dsl, config.getTypeToken());
this.config = config;
this.valueFunction = valueFunction;
}
Expand Down Expand Up @@ -536,6 +537,9 @@ private static Supplier<ConfigValuePair> newRandomConfigValueSupplier() {
}

static Maybe<?> execDslImmediately(final BrooklynDslDeferredSupplier<?> dsl, final Class<?> type, final Entity context, boolean execInTask) throws Exception {
return execDslImmediately(dsl, TypeToken.of(type), context, execInTask);
}
static Maybe<?> execDslImmediately(final BrooklynDslDeferredSupplier<?> dsl, final TypeToken<?> type, final Entity context, boolean execInTask) throws Exception {
// Exec'ing immediately will call DSL in current thread. It needs to find the context entity,
// and does this using BrooklynTaskTags.getTargetOrContextEntity(Tasks.current()).
// If we are not in a task executed by the context entity, then this lookup will fail.
Expand All @@ -560,11 +564,14 @@ public Maybe<?> call() throws Exception {
}
}

static Maybe<?> execDslRealRealQuick(BrooklynDslDeferredSupplier<?> dsl, Class<?> type, Entity context) {
static Maybe<?> execDslRealRealQuick(BrooklynDslDeferredSupplier<?> dsl, TypeToken<?> type, Entity context) {
return execDslEventually(dsl, type, context, ValueResolver.REAL_REAL_QUICK_WAIT);
}

static Maybe<?> execDslEventually(BrooklynDslDeferredSupplier<?> dsl, Class<?> type, Entity context, Duration timeout) {
return execDslEventually(dsl, TypeToken.of(type), context, timeout);
}
static Maybe<?> execDslEventually(BrooklynDslDeferredSupplier<?> dsl, TypeToken<?> type, Entity context, Duration timeout) {
return Tasks.resolving(dsl).as(type)
.context(context)
.description("Computing "+dsl)
Expand Down
Expand Up @@ -296,7 +296,7 @@ public BasicConfigKey(Builder<T,?> builder) {
}

/** @see ConfigKey#getTypeName() */
@Override public String getTypeName() { return getType().getName(); }
@Override public String getTypeName() { return getTypeToken().toString(); }

/** @see ConfigKey#getType() */
@Override public Class<? super T> getType() { return TypeTokens.getRawType(typeToken, type); }
Expand Down Expand Up @@ -444,7 +444,7 @@ protected Object resolveValue(Object v, ExecutionContext exec) throws ExecutionE
if (ValueResolver.supportsDeepResolution(v)) {
return Tasks.resolveDeepValue(v, Object.class, exec, "Resolving deep config "+name);
} else {
return Tasks.resolveValue(v, getType(), exec, "Resolving config "+name);
return Tasks.resolveValue(v, getTypeToken(), exec, "Resolving config "+name);
}
}

Expand Down
Expand Up @@ -34,6 +34,7 @@
import org.apache.brooklyn.core.objs.BrooklynObjectInternal;
import org.apache.brooklyn.core.objs.BrooklynObjectPredicate;
import org.apache.brooklyn.util.core.task.Tasks;
import org.apache.brooklyn.util.exceptions.ReferenceWithError;
import org.apache.brooklyn.util.guava.Maybe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -80,14 +81,17 @@ public static void assertValid(EntityAdjunct adjunct) {
}

public static <T> void assertValid(Entity entity, ConfigKey<T> key, T value) {
if (!new EntityConfigConstraints(entity).isValueValid(key, value)) {
throw new ConstraintViolationException("Invalid value for " + key + " on " + entity + ": " + value);
}
assertValid(new EntityConfigConstraints(entity), entity, key, value);
}

public static <T> void assertValid(Location location, ConfigKey<T> key, T value) {
if (!new LocationConfigConstraints(location).isValueValid(key, value)) {
throw new ConstraintViolationException("Invalid value for " + key + " on " + location + ": " + value);
assertValid(new LocationConfigConstraints(location), location, key, value);
}

private static <T> void assertValid(ConfigConstraints<?> constrants, Object context, ConfigKey<T> key, T value) {
ReferenceWithError<Predicate<?>> validity = constrants.validateValue(key, value);
if (validity.hasError()) {
throw new ConstraintViolationException("Invalid value for " + key + " on " + context + " (" + value + "); it should satisfy "+validity.getWithoutError());
}
}

Expand Down Expand Up @@ -149,19 +153,28 @@ protected Iterable<ConfigKey<?>> validateAll() {
return violating;
}

@SuppressWarnings("unchecked")
<V> boolean isValueValid(ConfigKey<V> configKey, V value) {
return !validateValue(configKey, value).hasError();
}

/** returns reference to null without error if valid; otherwise returns reference to predicate and a good error message */
@SuppressWarnings("unchecked")
<V> ReferenceWithError<Predicate<?>> validateValue(ConfigKey<V> configKey, V value) {
try {
Predicate<? super V> po = configKey.getConstraint();
boolean valid;
if (po instanceof BrooklynObjectPredicate) {
return BrooklynObjectPredicate.class.cast(po).apply(value, brooklynObject);
valid = BrooklynObjectPredicate.class.cast(po).apply(value, brooklynObject);
} else {
return po.apply(value);
valid = po.apply(value);
}
if (!valid) {
return ReferenceWithError.newInstanceThrowingError(po, new IllegalStateException("Invalid value for " + configKey.getName() + ": " + value));
}
} catch (Exception e) {
LOG.debug("Error checking constraint on " + configKey.getName(), e);
}
return true;
return ReferenceWithError.newInstanceWithoutError(null);
}

private BrooklynObjectInternal.ConfigurationSupportInternal getConfigurationSupportInternal() {
Expand Down
Expand Up @@ -32,6 +32,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.reflect.TypeParameter;
import com.google.common.reflect.TypeToken;

/** A config key representing a list of values.
Expand Down Expand Up @@ -63,16 +64,21 @@ public class ListConfigKey<V> extends AbstractCollectionConfigKey<List<V>,List<O
@SuppressWarnings("unused")
private static final Logger log = LoggerFactory.getLogger(ListConfigKey.class);

@SuppressWarnings("serial")
private static <V> TypeToken<List<V>> typeTokenFor(TypeToken<V> subType) {
return new TypeToken<List<V>>() {}
.where(new TypeParameter<V>() {}, subType);
}

public static class Builder<V> extends BasicConfigKey.Builder<List<V>,Builder<V>> {
protected Class<V> subType;
protected TypeToken<V> subType;

public Builder(TypeToken<V> subType, String name) {
super(new TypeToken<List<V>>() {}, name);
this.subType = (Class<V>) subType.getRawType();
super(typeTokenFor(subType), name);
this.subType = checkNotNull(subType);
}
public Builder(Class<V> subType, String name) {
super(new TypeToken<List<V>>() {}, name);
this.subType = checkNotNull(subType, "subType");
this(TypeToken.of(subType), name);
}
public Builder(ListConfigKey<V> key) {
this(key.getName(), key);
Expand Down Expand Up @@ -110,17 +116,29 @@ public ListConfigKey(Builder<V> builder) {
super(builder, builder.subType);
}

public ListConfigKey(Class<V> subType, String name) {
public ListConfigKey(TypeToken<V> subType, String name) {
this(subType, name, name, null);
}

public ListConfigKey(Class<V> subType, String name, String description) {
public ListConfigKey(TypeToken<V> subType, String name, String description) {
this(subType, name, description, null);
}

@SuppressWarnings({ "unchecked", "rawtypes" })
@SuppressWarnings({ "unchecked" })
public ListConfigKey(TypeToken<V> subType, String name, String description, List<? extends V> defaultValue) {
super(typeTokenFor(subType), subType, name, description, (List<V>)defaultValue);
}

public ListConfigKey(Class<V> subType, String name) {
this(TypeToken.of(subType), name);
}

public ListConfigKey(Class<V> subType, String name, String description) {
this(TypeToken.of(subType), name, description);
}

public ListConfigKey(Class<V> subType, String name, String description, List<? extends V> defaultValue) {
super((Class)List.class, subType, name, description, (List<V>)defaultValue);
this(TypeToken.of(subType), name, description, defaultValue);
}

@Override
Expand All @@ -143,7 +161,7 @@ public static class ListModifications extends StructuredModifications {
/** when passed as a value to a ListConfigKey, causes each of these items to be added.
* if you have just one, no need to wrap in a mod. */
// to prevent confusion (e.g. if a list is passed) we require two objects here.
public static final <T> ListModification<T> add(final T o1, final T o2, final T ...oo) {
public static final <T> ListModification<T> add(final T o1, final T o2, @SuppressWarnings("unchecked") final T ...oo) {
List<T> l = new ArrayList<T>();
l.add(o1); l.add(o2);
for (T o: oo) l.add(o);
Expand Down

0 comments on commit 002be99

Please sign in to comment.