You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by sj...@apache.org on 2015/10/14 15:55:47 UTC

[01/17] incubator-brooklyn git commit: Validation of config constraints on policies and enrichers.

Repository: incubator-brooklyn
Updated Branches:
  refs/heads/master 206d78256 -> 2a004d939


Validation of config constraints on policies and enrichers.


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

Branch: refs/heads/master
Commit: 8ff866b0fad456f5c856f59738935e9f765d8959
Parents: 87b4b9b
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Fri Aug 28 11:37:33 2015 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Thu Oct 8 11:10:31 2015 +0100

----------------------------------------------------------------------
 .../brooklyn/core/config/BasicConfigKey.java    |  1 +
 .../brooklyn/core/config/ConfigConstraints.java | 75 ++++++++++++++++----
 .../brooklyn/core/entity/AbstractEntity.java    |  2 -
 .../core/objs/AbstractEntityAdjunct.java        |  2 +-
 .../core/objs/proxy/InternalPolicyFactory.java  | 16 +++--
 .../core/config/ConfigKeyConstraintTest.java    | 67 ++++++++++++++---
 6 files changed, 130 insertions(+), 33 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8ff866b0/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java b/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
index 239e7dd..9056eac 100644
--- a/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
+++ b/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
@@ -112,6 +112,7 @@ public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializab
         public Builder<T> inheritance(ConfigInheritance val) {
             this.inheritance = val; return this;
         }
+        @Beta
         public Builder<T> constraint(Predicate<? super T> constraint) {
             this.constraint = checkNotNull(constraint, "constraint"); return this;
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8ff866b0/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java b/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
index 0ac030f..ce6f39b 100644
--- a/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
+++ b/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
@@ -22,8 +22,11 @@ package org.apache.brooklyn.core.config;
 import java.util.List;
 
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.objs.BrooklynObject;
+import org.apache.brooklyn.api.objs.EntityAdjunct;
 import org.apache.brooklyn.config.ConfigKey;
-import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.core.objs.AbstractEntityAdjunct;
+import org.apache.brooklyn.core.objs.BrooklynObjectInternal;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -31,25 +34,35 @@ import com.google.common.base.Predicate;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 
-public class ConfigConstraints {
+public abstract class ConfigConstraints<T extends BrooklynObject> {
 
     public static final Logger LOG = LoggerFactory.getLogger(ConfigConstraints.class);
-    private final Entity entity;
 
-    public ConfigConstraints(Entity e) {
-        this.entity = e;
-    }
+    private final T brooklynObject;
 
     /**
      * Checks all constraints of all config keys available to an entity.
      */
-    public static void assertValid(Entity e) {
-        Iterable<ConfigKey<?>> violations = new ConfigConstraints(e).getViolations();
+    public static void assertValid(Entity entity) {
+        Iterable<ConfigKey<?>> violations = new EntityConfigConstraints(entity).getViolations();
+        if (!Iterables.isEmpty(violations)) {
+            throw new ConstraintViolationException("ConfigKeys violate constraints: " + violations);
+        }
+    }
+
+    public static void assertValid(EntityAdjunct adjunct) {
+        Iterable<ConfigKey<?>> violations = new EntityAdjunctConstraints(adjunct).getViolations();
         if (!Iterables.isEmpty(violations)) {
-            throw new AssertionError("ConfigKeys violate constraints: " + violations);
+            throw new ConstraintViolationException("ConfigKeys violate constraints: " + violations);
         }
     }
 
+    public ConfigConstraints(T brooklynObject) {
+        this.brooklynObject = brooklynObject;
+    }
+
+    abstract Iterable<ConfigKey<?>> getBrooklynObjectTypeConfigKeys();
+
     public boolean isValid() {
         return Iterables.isEmpty(getViolations());
     }
@@ -60,12 +73,14 @@ public class ConfigConstraints {
 
     @SuppressWarnings("unchecked")
     private Iterable<ConfigKey<?>> validateAll() {
-        EntityInternal ei = (EntityInternal) entity;
         List<ConfigKey<?>> violating = Lists.newArrayList();
+        BrooklynObjectInternal.ConfigurationSupportInternal configInternal = getConfigurationSupportInternal();
 
-        for (ConfigKey<?> configKey : getEntityConfigKeys(entity)) {
+        Iterable<ConfigKey<?>> configKeys = getBrooklynObjectTypeConfigKeys();
+        LOG.trace("Checking config keys on {}: {}", getBrooklynObject(), configKeys);
+        for (ConfigKey<?> configKey : configKeys) {
             // getRaw returns null if explicitly set and absent if config key was unset.
-            Object value = ei.config().getRaw(configKey).or(configKey.getDefaultValue());
+            Object value = configInternal.getRaw(configKey).or(configKey.getDefaultValue());
 
             if (value == null || value.getClass().isAssignableFrom(configKey.getType())) {
                 // Cast should be safe because the author of the constraint on the config key had to
@@ -83,8 +98,40 @@ public class ConfigConstraints {
         return violating;
     }
 
-    private static Iterable<ConfigKey<?>> getEntityConfigKeys(Entity entity) {
-        return entity.getEntityType().getConfigKeys();
+    private BrooklynObjectInternal.ConfigurationSupportInternal getConfigurationSupportInternal() {
+        return ((BrooklynObjectInternal) brooklynObject).config();
+    }
+
+    protected T getBrooklynObject() {
+        return brooklynObject;
+    }
+
+    private static class EntityConfigConstraints extends ConfigConstraints<Entity> {
+        public EntityConfigConstraints(Entity brooklynObject) {
+            super(brooklynObject);
+        }
+
+        @Override
+        Iterable<ConfigKey<?>> getBrooklynObjectTypeConfigKeys() {
+            return getBrooklynObject().getEntityType().getConfigKeys();
+        }
+    }
+
+    private static class EntityAdjunctConstraints extends ConfigConstraints<EntityAdjunct> {
+        public EntityAdjunctConstraints(EntityAdjunct brooklynObject) {
+            super(brooklynObject);
+        }
+
+        @Override
+        Iterable<ConfigKey<?>> getBrooklynObjectTypeConfigKeys() {
+            return ((AbstractEntityAdjunct) getBrooklynObject()).getAdjunctType().getConfigKeys();
+        }
+    }
+
+    public static class ConstraintViolationException extends RuntimeException {
+        public ConstraintViolationException(String message) {
+            super(message);
+        }
     }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8ff866b0/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
index d91b28f..eb919a7 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
@@ -1551,8 +1551,6 @@ public abstract class AbstractEntity extends AbstractBrooklynObject implements E
      */
     protected ToStringHelper toStringHelper() {
         return Objects.toStringHelper(this).omitNullValues().add("id", getId());
-//            make output more concise by suppressing display name
-//            .add("name", getDisplayName());
     }
     
     // -------- INITIALIZATION --------------

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8ff866b0/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
index 8200ea8..3434471 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
@@ -426,7 +426,7 @@ public abstract class AbstractEntityAdjunct extends AbstractBrooklynObject imple
     
     protected abstract void onChanged();
     
-    protected AdjunctType getAdjunctType() {
+    public AdjunctType getAdjunctType() {
         return adjunctType;
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8ff866b0/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalPolicyFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalPolicyFactory.java b/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalPolicyFactory.java
index aaee778..370d6fc 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalPolicyFactory.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalPolicyFactory.java
@@ -20,13 +20,17 @@ package org.apache.brooklyn.core.objs.proxy;
 
 import java.util.Map;
 
+import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.api.objs.BrooklynObject;
+import org.apache.brooklyn.api.objs.EntityAdjunct;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
 import org.apache.brooklyn.api.sensor.Enricher;
 import org.apache.brooklyn.api.sensor.EnricherSpec;
 import org.apache.brooklyn.api.sensor.Feed;
 import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigConstraints;
 import org.apache.brooklyn.core.enricher.AbstractEnricher;
 import org.apache.brooklyn.core.entity.AbstractEntity;
 import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
@@ -98,15 +102,15 @@ public class InternalPolicyFactory extends InternalFactory {
         if (spec.getFlags().containsKey("parent")) {
             throw new IllegalArgumentException("Spec's flags must not contain parent; use spec.parent() instead for "+spec);
         }
-        
+
         try {
             Class<? extends T> clazz = spec.getType();
-            
+
             T pol = construct(clazz, spec.getFlags());
 
-            if (spec.getDisplayName()!=null)
+            if (spec.getDisplayName()!=null) {
                 ((AbstractPolicy)pol).setDisplayName(spec.getDisplayName());
-            
+            }
             if (spec.getCatalogItemId()!=null) {
                 ((AbstractPolicy)pol).setCatalogItemId(spec.getCatalogItemId());
             }
@@ -125,6 +129,7 @@ public class InternalPolicyFactory extends InternalFactory {
             for (Map.Entry<ConfigKey<?>, Object> entry : spec.getConfig().entrySet()) {
                 pol.config().set((ConfigKey)entry.getKey(), entry.getValue());
             }
+            ConfigConstraints.assertValid(pol);
             ((AbstractPolicy)pol).init();
             
             return pol;
@@ -166,6 +171,7 @@ public class InternalPolicyFactory extends InternalFactory {
             for (Map.Entry<ConfigKey<?>, Object> entry : spec.getConfig().entrySet()) {
                 enricher.config().set((ConfigKey)entry.getKey(), entry.getValue());
             }
+            ConfigConstraints.assertValid(enricher);
             ((AbstractEnricher)enricher).init();
             
             return enricher;
@@ -174,7 +180,7 @@ public class InternalPolicyFactory extends InternalFactory {
             throw Exceptions.propagate(e);
         }
     }
-    
+
     /**
      * Constructs a new-style policy (fails if no no-arg constructor).
      */

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8ff866b0/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java b/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
index 130e3a7..6636944 100644
--- a/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
@@ -24,17 +24,22 @@ import static org.testng.Assert.fail;
 
 import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.api.sensor.EnricherSpec;
 import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.enricher.AbstractEnricher;
+import org.apache.brooklyn.core.policy.AbstractPolicy;
 import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
 import org.apache.brooklyn.core.test.entity.TestEntity;
 import org.apache.brooklyn.core.test.entity.TestEntityImpl;
 import org.apache.brooklyn.core.test.policy.TestPolicy;
 import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.net.Networking;
 import org.testng.annotations.Test;
+import org.apache.brooklyn.core.config.ConfigConstraints.ConstraintViolationException;
 
 import com.google.common.base.Predicates;
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Range;
 
 public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
@@ -46,7 +51,6 @@ public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
                 .description("Configuration key that must not be null")
                 .constraint(Predicates.notNull())
                 .build();
-
     }
 
     @ImplementedBy(EntityWithNonNullConstraintWithNonNullDefaultImpl.class)
@@ -86,13 +90,30 @@ public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
     public static class EntityProvidingDefaultValueForConfigKeyInRangeImpl extends TestEntityImpl implements EntityProvidingDefaultValueForConfigKeyInRange {
     }
 
+    public static class PolicyWithConfigConstraint extends AbstractPolicy{
+        public static final ConfigKey<Object> NON_NULL_CONFIG = ConfigKeys.builder(Object.class)
+                .name("test.policy.non-null")
+                .description("Configuration key that must not be null")
+                .constraint(Predicates.notNull())
+                .build();
+    }
+
+    public static class EnricherWithConfigConstraint extends AbstractEnricher {
+        public static final ConfigKey<String> PATTERN = ConfigKeys.builder(String.class)
+                .name("test.enricher.regex")
+                .description("Must match a valid IPv4 address")
+                .constraint(Predicates.containsPattern(Networking.VALID_IP_ADDRESS_REGEX))
+                .build();
+    }
+
+
     @Test
     public void testExceptionWhenEntityHasNullConfig() {
         try {
             app.createAndManageChild(EntitySpec.create(EntityWithNonNullConstraint.class));
             fail("Expected exception when managing entity with missing config");
         } catch (Exception e) {
-            Throwable t = Exceptions.getFirstThrowableOfType(e, AssertionError.class);
+            Throwable t = Exceptions.getFirstThrowableOfType(e, ConstraintViolationException.class);
             assertNotNull(t);
         }
     }
@@ -114,7 +135,7 @@ public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
             app.createAndManageChild(EntitySpec.create(EntityProvidingDefaultValueForConfigKeyInRange.class));
             fail("Expected exception when managing entity setting invalid default value");
         } catch (Exception e) {
-            Throwable t = Exceptions.getFirstThrowableOfType(e, AssertionError.class);
+            Throwable t = Exceptions.getFirstThrowableOfType(e, ConstraintViolationException.class);
             assertNotNull(t);
         }
     }
@@ -126,23 +147,47 @@ public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
                     .configure(EntityWithNonNullConstraintWithNonNullDefault.NON_NULL_WITH_DEFAULT, (Object) null));
             fail("Expected exception when config key set to null");
         } catch (Exception e) {
-            Throwable t = Exceptions.getFirstThrowableOfType(e, AssertionError.class);
+            Throwable t = Exceptions.getFirstThrowableOfType(e, ConstraintViolationException.class);
             assertNotNull(t);
         }
     }
 
+    // Test fails because config keys that are not on an object's interfaces cannot be checked automatically.
     @Test(enabled = false)
+    public void testExceptionWhenPolicyHasNullForeignConfig() {
+        Policy p = mgmt.getEntityManager().createPolicy(PolicySpec.create(TestPolicy.class)
+                .configure(EntityWithNonNullConstraint.NON_NULL_CONFIG, (Object) null));
+        try {
+            ConfigConstraints.assertValid(p);
+            fail("Expected exception when validating policy with missing config");
+        } catch (Exception e) {
+            Throwable t = Exceptions.getFirstThrowableOfType(e, ConstraintViolationException.class);
+            assertNotNull(t);
+        }
+    }
+
+    @Test
     public void testExceptionWhenPolicyHasNullConfig() {
-        app.createAndManageChild(EntitySpec.create(TestEntity.class)
-                .policy(PolicySpec.create(TestPolicy.class)
-                        .configure(EntityWithNonNullConstraint.NON_NULL_CONFIG, (Object) null)));
         try {
-            app.start(ImmutableList.of(app.newSimulatedLocation()));
-            fail("Expected exception when starting entity with policy with missing config");
+            mgmt.getEntityManager().createPolicy(PolicySpec.create(PolicyWithConfigConstraint.class)
+                    .configure(PolicyWithConfigConstraint.NON_NULL_CONFIG, (Object) null));
+            fail("Expected exception when creating policy with missing config");
         } catch (Exception e) {
-            Throwable t = Exceptions.getFirstThrowableOfType(e, AssertionError.class);
+            Throwable t = Exceptions.getFirstThrowableOfType(e, ConstraintViolationException.class);
             assertNotNull(t);
         }
     }
 
+    @Test
+    public void testExceptionWhenEnricherHasInvalidConfig() {
+        try {
+            mgmt.getEntityManager().createEnricher(EnricherSpec.create(EnricherWithConfigConstraint.class)
+                    .configure(EnricherWithConfigConstraint.PATTERN, "123.123.256.10"));
+            fail("Expected exception when creating enricher with invalid config");
+        } catch (Exception e) {
+            Throwable t = Exceptions.getFirstThrowableOfType(e, ConstraintViolationException.class);
+            assertNotNull(t, "Exception was: " + Exceptions.collapseText(e));
+        }
+    }
+
 }


[13/17] incubator-brooklyn git commit: Amend text in creating-yaml.md

Posted by sj...@apache.org.
Amend text in creating-yaml.md

Amended paragraph in section "The Basic Structure" to clarify reference to the copy icon and to add some apparently missing text.

Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/9751e027
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/9751e027
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/9751e027

Branch: refs/heads/master
Commit: 9751e0271c5433601c6888d2f1279c7117fbd859
Parents: 94687d9
Author: Geoff Macartney <ge...@users.noreply.github.com>
Authored: Wed Oct 14 14:26:19 2015 +0100
Committer: Geoff Macartney <ge...@users.noreply.github.com>
Committed: Wed Oct 14 14:26:19 2015 +0100

----------------------------------------------------------------------
 docs/guide/yaml/creating-yaml.md | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9751e027/docs/guide/yaml/creating-yaml.md
----------------------------------------------------------------------
diff --git a/docs/guide/yaml/creating-yaml.md b/docs/guide/yaml/creating-yaml.md
index 8c6b31f..ffb1e36 100644
--- a/docs/guide/yaml/creating-yaml.md
+++ b/docs/guide/yaml/creating-yaml.md
@@ -31,10 +31,10 @@ Here's a very simple YAML blueprint plan, to explain the structure:
 * The `services` block takes a list of the typed services we want to deploy.
   This is the meat of the blueprint plan, as you'll see below.
 
-Finally, that clipboard in the corner lets you easily copy-and-paste into the web-console:
+Finally, the clipboard in the top-right corner of the example plan box above (hover your cursor over the box)  lets you easily copy-and-paste into the web-console:
 simply [download and launch]({{ site.path.guide }}/start/running.html) Brooklyn,
-then in the "Add Application" dialog at the web console
-(usually [http://127.0.0.1:8081/](http://127.0.0.1:8081/). 
+then in the "Create Application" dialog at the web console
+(usually [http://127.0.0.1:8081/](http://127.0.0.1:8081/), paste the copied YAML into the "Yaml" tab of the dialog and press "Finish". 
 There are several other ways to deploy, including `curl` and via the command-line,
 and you can configure users, https, persistence, and more, 
 as described [in the ops guide](../ops/).


[10/17] incubator-brooklyn git commit: Validate config().set(ConfigKey, T) on entities, adjuncts and locations

Posted by sj...@apache.org.
Validate config().set(ConfigKey<T>, T) on entities, adjuncts and locations


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/9ba5391d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/9ba5391d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/9ba5391d

Branch: refs/heads/master
Commit: 9ba5391d4db5ed0c2b3a6ea867810276d8b73e1e
Parents: aa5c00f
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Fri Oct 9 16:57:33 2015 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Fri Oct 9 16:57:33 2015 +0100

----------------------------------------------------------------------
 .../brooklyn/core/config/ConfigConstraints.java | 69 +++++++++++++-------
 .../brooklyn/core/entity/AbstractEntity.java    |  2 +
 .../core/location/AbstractLocation.java         |  2 +
 .../core/objs/AbstractEntityAdjunct.java        |  2 +
 .../core/config/ConfigKeyConstraintTest.java    | 27 ++++++++
 5 files changed, 80 insertions(+), 22 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9ba5391d/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java b/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
index b1afe75..1a273f6 100644
--- a/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
+++ b/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
@@ -19,13 +19,16 @@
 
 package org.apache.brooklyn.core.config;
 
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.objs.BrooklynObject;
 import org.apache.brooklyn.api.objs.EntityAdjunct;
 import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.location.AbstractLocation;
 import org.apache.brooklyn.core.objs.AbstractEntityAdjunct;
 import org.apache.brooklyn.core.objs.BrooklynObjectPredicate;
 import org.apache.brooklyn.core.objs.BrooklynObjectInternal;
@@ -74,6 +77,18 @@ public abstract class ConfigConstraints<T extends BrooklynObject> {
         }
     }
 
+    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);
+        }
+    }
+
+    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);
+        }
+    }
+
     private static String errorMessage(BrooklynObject object, Iterable<ConfigKey<?>> violations) {
         StringBuilder message = new StringBuilder("Error configuring ")
                 .append(object.getDisplayName())
@@ -97,10 +112,6 @@ public abstract class ConfigConstraints<T extends BrooklynObject> {
 
     abstract Iterable<ConfigKey<?>> getBrooklynObjectTypeConfigKeys();
 
-    public boolean isValid() {
-        return Iterables.isEmpty(getViolations());
-    }
-
     public Iterable<ConfigKey<?>> getViolations() {
         return validateAll();
     }
@@ -108,36 +119,39 @@ public abstract class ConfigConstraints<T extends BrooklynObject> {
     @SuppressWarnings("unchecked")
     private Iterable<ConfigKey<?>> validateAll() {
         List<ConfigKey<?>> violating = Lists.newLinkedList();
-        BrooklynObjectInternal.ConfigurationSupportInternal configInternal = getConfigurationSupportInternal();
-
         Iterable<ConfigKey<?>> configKeys = getBrooklynObjectTypeConfigKeys();
         LOG.trace("Checking config keys on {}: {}", getBrooklynObject(), configKeys);
         for (ConfigKey<?> configKey : configKeys) {
+            BrooklynObjectInternal.ConfigurationSupportInternal configInternal = getConfigurationSupportInternal();
             // getNonBlocking method coerces the value to the config key's type.
             Maybe<?> maybeValue = configInternal.getNonBlocking(configKey);
             if (maybeValue.isPresent()) {
-                Object value = maybeValue.get();
-                try {
-                    // Cast is safe because the author of the constraint on the config key had to
-                    // keep its type to Predicte<? super T>, where T is ConfigKey<T>.
-                    Predicate<Object> po = (Predicate<Object>) configKey.getConstraint();
-                    boolean isValid;
-                    if (po instanceof BrooklynObjectPredicate) {
-                        isValid = BrooklynObjectPredicate.class.cast(po).apply(value, brooklynObject);
-                    } else {
-                        isValid = po.apply(value);
-                    }
-                    if (!isValid) {
-                        violating.add(configKey);
-                    }
-                } catch (Exception e) {
-                    LOG.debug("Error checking constraint on " + configKey.getName(), e);
+                // Cast is safe because the author of the constraint on the config key had to
+                // keep its type to Predicte<? super T>, where T is ConfigKey<T>.
+                ConfigKey<Object> ck = (ConfigKey<Object>) configKey;
+                if (!isValueValid(ck, maybeValue.get())) {
+                    violating.add(configKey);
                 }
             }
         }
         return violating;
     }
 
+    @SuppressWarnings("unchecked")
+    <V> boolean isValueValid(ConfigKey<V> configKey, V value) {
+        try {
+            Predicate<? super V> po = configKey.getConstraint();
+            if (po instanceof BrooklynObjectPredicate) {
+                return BrooklynObjectPredicate.class.cast(po).apply(value, brooklynObject);
+            } else {
+                return po.apply(value);
+            }
+        } catch (Exception e) {
+            LOG.debug("Error checking constraint on " + configKey.getName(), e);
+        }
+        return true;
+    }
+
     private BrooklynObjectInternal.ConfigurationSupportInternal getConfigurationSupportInternal() {
         return ((BrooklynObjectInternal) brooklynObject).config();
     }
@@ -168,4 +182,15 @@ public abstract class ConfigConstraints<T extends BrooklynObject> {
         }
     }
 
+    private static class LocationConfigConstraints extends ConfigConstraints<Location> {
+        public LocationConfigConstraints(Location brooklynObject) {
+            super(brooklynObject);
+        }
+
+        @Override
+        Iterable<ConfigKey<?>> getBrooklynObjectTypeConfigKeys() {
+            return Collections.emptyList();
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9ba5391d/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
index a2e12f4..49afc60 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
@@ -58,6 +58,7 @@ import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
 import org.apache.brooklyn.core.BrooklynFeatureEnablement;
 import org.apache.brooklyn.core.BrooklynLogging;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
+import org.apache.brooklyn.core.config.ConfigConstraints;
 import org.apache.brooklyn.core.config.render.RendererHints;
 import org.apache.brooklyn.core.enricher.AbstractEnricher;
 import org.apache.brooklyn.core.entity.internal.EntityConfigMap;
@@ -1144,6 +1145,7 @@ public abstract class AbstractEntity extends AbstractBrooklynObject implements E
 
         @Override
         public <T> T set(ConfigKey<T> key, T val) {
+            ConfigConstraints.assertValid(AbstractEntity.this, key, val);
             return setConfigInternal(key, val);
         }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9ba5391d/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java b/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java
index 0fd3f7b..98a7e38 100644
--- a/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java
+++ b/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java
@@ -47,6 +47,7 @@ import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
 import org.apache.brooklyn.core.BrooklynFeatureEnablement;
 import org.apache.brooklyn.core.config.BasicConfigKey;
+import org.apache.brooklyn.core.config.ConfigConstraints;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.internal.storage.BrooklynStorage;
 import org.apache.brooklyn.core.internal.storage.Reference;
@@ -400,6 +401,7 @@ public abstract class AbstractLocation extends AbstractBrooklynObject implements
 
         @Override
         public <T> T set(ConfigKey<T> key, T val) {
+            ConfigConstraints.assertValid(AbstractLocation.this, key, val);
             T result = configBag.put(key, val);
             onChanged();
             return result;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9ba5391d/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
index a7b00f4..3fc2839 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
@@ -42,6 +42,7 @@ import org.apache.brooklyn.api.sensor.Sensor;
 import org.apache.brooklyn.api.sensor.SensorEventListener;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.config.ConfigMap;
+import org.apache.brooklyn.core.config.ConfigConstraints;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.enricher.AbstractEnricher;
 import org.apache.brooklyn.core.entity.Entities;
@@ -298,6 +299,7 @@ public abstract class AbstractEntityAdjunct extends AbstractBrooklynObject imple
         @SuppressWarnings("unchecked")
         @Override
         public <T> T set(ConfigKey<T> key, T val) {
+            ConfigConstraints.assertValid(entity, key, val);
             if (entity != null && isRunning()) {
                 doReconfigureConfig(key, val);
             }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9ba5391d/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java b/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
index 8a9e1c6..e3bfe2d 100644
--- a/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
@@ -27,6 +27,7 @@ import java.util.concurrent.Callable;
 
 import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.mgmt.Task;
 import org.apache.brooklyn.api.objs.BrooklynObject;
 import org.apache.brooklyn.api.policy.Policy;
@@ -35,9 +36,12 @@ import org.apache.brooklyn.api.sensor.EnricherSpec;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.enricher.AbstractEnricher;
 import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.factory.ApplicationBuilder;
 import org.apache.brooklyn.core.objs.BrooklynObjectPredicate;
 import org.apache.brooklyn.core.policy.AbstractPolicy;
 import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.core.test.entity.TestApplication;
 import org.apache.brooklyn.core.test.entity.TestEntity;
 import org.apache.brooklyn.core.test.entity.TestEntityImpl;
 import org.apache.brooklyn.core.test.policy.TestPolicy;
@@ -46,6 +50,7 @@ import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.net.Networking;
 import org.apache.brooklyn.util.time.Duration;
 import org.apache.brooklyn.util.time.Time;
+import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
 import com.google.common.base.Predicate;
@@ -319,4 +324,26 @@ public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
                 .build();
     }
 
+    // Supplies an entity, a policy and a location.
+    @DataProvider(name = "brooklynObjects")
+    public Object[][] createBrooklynObjects() {
+        TestApplication app = ApplicationBuilder.newManagedApp(EntitySpec.create(TestApplication.class), LocalManagementContextForTests.newInstance());
+        EntityRequiringConfigKeyInRange entity = app.createAndManageChild(EntitySpec.create(EntityRequiringConfigKeyInRange.class)
+                .configure(EntityRequiringConfigKeyInRange.RANGE, 5));
+        Policy policy = entity.policies().add(PolicySpec.create(TestPolicy.class));
+        Location location = app.newSimulatedLocation();
+        return new Object[][]{{entity}, {policy}, {location}};
+    }
+
+    @Test(dataProvider = "brooklynObjects")
+    public void testCannotUpdateConfigToInvalidValue(BrooklynObject object) {
+        try {
+            object.config().set(EntityRequiringConfigKeyInRange.RANGE, -1);
+            fail("Expected exception when calling config().set with invalid value on " + object);
+        } catch (Exception e) {
+            Throwable t = Exceptions.getFirstThrowableOfType(e, ConstraintViolationException.class);
+            assertNotNull(t, "Original exception was: " + Exceptions.collapseText(e));
+        }
+    }
+
 }


[15/17] incubator-brooklyn git commit: This closes #953

Posted by sj...@apache.org.
This closes #953


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/25d4f5f7
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/25d4f5f7
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/25d4f5f7

Branch: refs/heads/master
Commit: 25d4f5f7d9bea6127a2e895c5527d5d8e737cdff
Parents: 7efd062 e278257
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Wed Oct 14 14:50:29 2015 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Wed Oct 14 14:50:29 2015 +0100

----------------------------------------------------------------------
 .../main/java/org/apache/brooklyn/rest/api/EntityApi.java    | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
----------------------------------------------------------------------



[16/17] incubator-brooklyn git commit: This closes #890

Posted by sj...@apache.org.
This closes #890


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

Branch: refs/heads/master
Commit: 8e4b3bab9023fae37e59bf8042f0ba754760c92b
Parents: 25d4f5f 9ba5391
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Wed Oct 14 14:50:38 2015 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Wed Oct 14 14:50:38 2015 +0100

----------------------------------------------------------------------
 .../org/apache/brooklyn/api/entity/Entity.java  |   2 +-
 .../brooklyn/core/config/BasicConfigKey.java    |  41 ++-
 .../brooklyn/core/config/ConfigConstraints.java | 196 +++++++++++
 .../config/ConstraintViolationException.java    |  38 ++
 .../brooklyn/core/entity/AbstractEntity.java    |  38 +-
 .../core/location/AbstractLocation.java         |  37 +-
 .../mgmt/internal/EntityManagementSupport.java  |   5 +-
 .../core/mgmt/internal/LocalEntityManager.java  |  11 +-
 .../NonDeploymentManagementContext.java         |  18 +-
 .../AbstractConfigurationSupportInternal.java   |  90 +++++
 .../core/objs/AbstractEntityAdjunct.java        |  37 +-
 .../core/objs/BrooklynObjectInternal.java       |  32 +-
 .../core/objs/BrooklynObjectPredicate.java      |  33 ++
 .../core/objs/proxy/InternalEntityFactory.java  |  37 +-
 .../core/objs/proxy/InternalPolicyFactory.java  |  16 +-
 .../brooklyn/util/core/ResourcePredicates.java  |  61 ++++
 .../brooklyn/util/core/ResourceUtils.java       |  23 +-
 .../core/config/ConfigKeyConstraintTest.java    | 349 +++++++++++++++++++
 .../concepts/configuration-sensor-effectors.md  |   4 +-
 docs/guide/java/entities.md                     |  17 +-
 .../resources/AbstractBrooklynRestResource.java |  12 +-
 .../rest/resources/ApplicationResource.java     |   8 +-
 .../org/apache/brooklyn/config/ConfigKey.java   |  23 +-
 .../util/guava/PredicateWithContext.java        |  33 ++
 24 files changed, 1016 insertions(+), 145 deletions(-)
----------------------------------------------------------------------



[03/17] incubator-brooklyn git commit: Javadoc improvements

Posted by sj...@apache.org.
Javadoc improvements


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

Branch: refs/heads/master
Commit: f0e0ba7ea22b8465bba1a3e94d6ba1b5446ff322
Parents: 7b89b66
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Tue Aug 25 16:51:17 2015 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Thu Oct 8 11:10:31 2015 +0100

----------------------------------------------------------------------
 .../org/apache/brooklyn/api/entity/Entity.java  |  2 +-
 .../brooklyn/core/entity/AbstractEntity.java    |  2 +-
 .../brooklyn/util/core/ResourceUtils.java       | 23 +++++++++++++++-----
 .../org/apache/brooklyn/config/ConfigKey.java   |  4 ++--
 4 files changed, 21 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f0e0ba7e/api/src/main/java/org/apache/brooklyn/api/entity/Entity.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/brooklyn/api/entity/Entity.java b/api/src/main/java/org/apache/brooklyn/api/entity/Entity.java
index eb9f907..e01d5e2 100644
--- a/api/src/main/java/org/apache/brooklyn/api/entity/Entity.java
+++ b/api/src/main/java/org/apache/brooklyn/api/entity/Entity.java
@@ -265,7 +265,7 @@ public interface Entity extends BrooklynObject {
      * @deprecated since 0.9.0; see {@link PolicySupport#add(PolicySpec)}
      */
     @Deprecated
-    <T extends Policy> T addPolicy(PolicySpec<T> enricher);
+    <T extends Policy> T addPolicy(PolicySpec<T> policy);
     
     /**
      * Removes the given policy from this entity. 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f0e0ba7e/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
index b1b1d27..d91b28f 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
@@ -987,7 +987,7 @@ public abstract class AbstractEntity extends AbstractBrooklynObject implements E
 
     /**
      * Direct use of this class is strongly discouraged. It will become private in a future release,
-     * once {@link #sensors()} is reverted to return {@link SensorsSupport} instead of
+     * once {@link #sensors()} is reverted to return {@link SensorSupport} instead of
      * {@link BasicSensorSupport}.
      */
     @Beta

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f0e0ba7e/core/src/main/java/org/apache/brooklyn/util/core/ResourceUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/ResourceUtils.java b/core/src/main/java/org/apache/brooklyn/util/core/ResourceUtils.java
index 872332b..26c143d 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/ResourceUtils.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/ResourceUtils.java
@@ -478,26 +478,37 @@ public class ResourceUtils {
         }
     }
 
-    /** allows failing-fast if URL cannot be read */
+    /** @see #checkUrlExists(String, String) */
     public String checkUrlExists(String url) {
         return checkUrlExists(url, null);
     }
-    
+
+    /**
+     * Throws if the given URL cannot be read.
+     * @param url The URL to test
+     * @param message An optional message to use for the exception thrown.
+     * @return The URL argument
+     * @see #getResourceFromUrl(String)
+     */
     public String checkUrlExists(String url, String message) {
         if (url==null) throw new NullPointerException("URL "+(message!=null ? message+" " : "")+"must not be null");
-        InputStream s;
+        InputStream s = null;
         try {
             s = getResourceFromUrl(url);
         } catch (Exception e) {
             Exceptions.propagateIfFatal(e);
             throw new IllegalArgumentException("Unable to access URL "+(message!=null ? message : "")+": "+url, e);
+        } finally {
+            Streams.closeQuietly(s);
         }
-        Streams.closeQuietly(s); 
         return url;
     }
 
-    /** tests whether the url exists, returning true or false */
-    public boolean doesUrlExist(String url) {
+    /**
+     * @return True if the given URL can be read, false otherwise.
+     * @see #getResourceFromUrl(String)
+     */
+     public boolean doesUrlExist(String url) {
         InputStream s = null;
         try {
             s = getResourceFromUrl(url);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f0e0ba7e/utils/common/src/main/java/org/apache/brooklyn/config/ConfigKey.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/config/ConfigKey.java b/utils/common/src/main/java/org/apache/brooklyn/config/ConfigKey.java
index f2f31fe..3487bc4 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/config/ConfigKey.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/config/ConfigKey.java
@@ -55,8 +55,8 @@ public interface ConfigKey<T> {
      * <p> 
      * This returns a "super" of T only in the case where T is generified, 
      * and in such cases it returns the Class instance for the unadorned T ---
-     * i.e. for List<String> this returns Class<List> ---
-     * this is of course because there is no actual Class<List<String>> instance.
+     * i.e. for List&lt;String&gt; this returns Class&lt;List&gt; ---
+     * this is of course because there is no actual Class&lt;List&lt;String&gt;&gt; instance.
      */
     Class<? super T> getType();
 


[07/17] incubator-brooklyn git commit: Website documentation on constraints

Posted by sj...@apache.org.
Website documentation on constraints


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/781e2cfa
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/781e2cfa
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/781e2cfa

Branch: refs/heads/master
Commit: 781e2cfaa97d28ac796bb4c185a5412c0e490fda
Parents: 60d85fa
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Fri Sep 11 18:32:54 2015 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Thu Oct 8 17:54:43 2015 +0100

----------------------------------------------------------------------
 .../concepts/configuration-sensor-effectors.md     |  4 ++--
 docs/guide/java/entities.md                        | 17 +++++++++++++++--
 2 files changed, 17 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/781e2cfa/docs/guide/concepts/configuration-sensor-effectors.md
----------------------------------------------------------------------
diff --git a/docs/guide/concepts/configuration-sensor-effectors.md b/docs/guide/concepts/configuration-sensor-effectors.md
index bb41343..6363b8a 100644
--- a/docs/guide/concepts/configuration-sensor-effectors.md
+++ b/docs/guide/concepts/configuration-sensor-effectors.md
@@ -15,7 +15,7 @@ Configuration is propagated when an application "goes live" (i.e. it becomes "ma
 Configuration values can be specified in a configuration file (``~/.brooklyn/brooklyn.properties``)
 to apply universally, and/or programmatically to a specific entity and its descendants 
 by calling `.configure(KEY, VALUE)` in the entity spec when creating it.
-There is also an ``entity.setConfig(KEY, VALUE)`` method.
+There is also an ``entity.config().set(KEY, VALUE)`` method.
 
 Additionally, many common configuration parameters are available as "flags" which can be supplied as Strings when constructing
 then entity, in the form
@@ -35,6 +35,6 @@ Sensors can be updated by the entity or associated tasks, and sensors from an en
 Effectors can be invoked by an entity's parent remotely, and the invoker is able to track the execution of that effector. Effectors can be invoked by other entities, but use this functionality with care to prevent too many managers!
 
 An entity consists of a Java interface (used when interacting with the entity) and a Java class. For resilience. it is recommended to store 
-the entity's state in attributes (see `getAttribute(AttributeKey)``). If internal fields can be used then the data will be lost on brooklyn 
+the entity's state in attributes (see ``getAttribute(AttributeKey)``). If internal fields can be used then the data will be lost on brooklyn
 restart, and may cause problems if the entity is to be moved to a different brooklyn management node.
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/781e2cfa/docs/guide/java/entities.md
----------------------------------------------------------------------
diff --git a/docs/guide/java/entities.md b/docs/guide/java/entities.md
index b83c61a..b83bfc9 100644
--- a/docs/guide/java/entities.md
+++ b/docs/guide/java/entities.md
@@ -50,8 +50,21 @@ TODO: why to use config?
   Syntax is as described in the PortRange interface. For example, "8080-8099,8800+" will try port 8080, try sequentially through 8099, then try from 8800 until all ports are exhausted.
   
   This is particularly useful on a contended machine (localhost!). Like ordinary configuration, the config is done by the user, and the actual port used is reported back as a sensor on the entity.
- 
- 
+
+- Validation of config values can be applied by supplying a ``Predicate`` to the ``constraint`` of a ConfigKey builder.
+  Constraints are tested after an entity is initialised and before an entity managed.
+  Useful predicates include:
+  - ``StringPredicates.isNonBlank``: require that a String key is neither null nor empty.
+  - ``ResourcePredicates.urlExists``: require that a URL that is loadable by Brooklyn. Use this to
+    confirm that necessary resources are available to the entity.
+  - ``Predicates.in``: require one of a fixed set of values.
+  - ``Predicates.containsPattern``: require that a value match a regular expression pattern.
+
+  An important caveat is that only constraints on config keys that are on an entity's type hierarchy can be
+  tested automatically. Brooklyn has no knowledge of the true type of other keys until they are retrieved with a
+  ``config().get(key)``.
+
+
 Implementing Sensors
 --------------------
 


[06/17] incubator-brooklyn git commit: Adds ConfigurationSupportInternal.getNonBlocking

Posted by sj...@apache.org.
Adds ConfigurationSupportInternal.getNonBlocking

ConfigConstraints uses the method to give a grace period to tasks when
validating values.


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

Branch: refs/heads/master
Commit: aa5c00f6c6b148fb192786f127d0bac19dc0e3d1
Parents: 2f9b9e1
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Thu Oct 8 17:49:47 2015 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Thu Oct 8 17:54:43 2015 +0100

----------------------------------------------------------------------
 .../brooklyn/core/config/ConfigConstraints.java | 20 ++++---
 .../brooklyn/core/entity/AbstractEntity.java    |  4 ++
 .../core/location/AbstractLocation.java         |  6 +++
 .../AbstractConfigurationSupportInternal.java   | 37 +++++++++++++
 .../core/objs/AbstractEntityAdjunct.java        |  5 ++
 .../core/objs/BrooklynObjectInternal.java       | 28 +++++++---
 .../core/config/ConfigKeyConstraintTest.java    | 55 ++++++++++++++++----
 7 files changed, 132 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/aa5c00f6/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java b/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
index 6c6445f..b1afe75 100644
--- a/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
+++ b/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
@@ -29,6 +29,7 @@ import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.objs.AbstractEntityAdjunct;
 import org.apache.brooklyn.core.objs.BrooklynObjectPredicate;
 import org.apache.brooklyn.core.objs.BrooklynObjectInternal;
+import org.apache.brooklyn.util.guava.Maybe;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -36,6 +37,11 @@ import com.google.common.base.Predicate;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 
+/**
+ * Checks configuration constraints on entities and their adjuncts.
+ *
+ * @since 0.9.0
+ */
 public abstract class ConfigConstraints<T extends BrooklynObject> {
 
     public static final Logger LOG = LoggerFactory.getLogger(ConfigConstraints.class);
@@ -107,13 +113,13 @@ public abstract class ConfigConstraints<T extends BrooklynObject> {
         Iterable<ConfigKey<?>> configKeys = getBrooklynObjectTypeConfigKeys();
         LOG.trace("Checking config keys on {}: {}", getBrooklynObject(), configKeys);
         for (ConfigKey<?> configKey : configKeys) {
-            // getRaw returns null if explicitly set and absent if config key was unset.
-            Object value = configInternal.getRaw(configKey).or(configKey.getDefaultValue());
-
-            if (value == null || value.getClass().isAssignableFrom(configKey.getType())) {
-                // Cast should be safe because the author of the constraint on the config key had to
-                // keep its type to Predicte<? super T>, where T is ConfigKey<T>.
+            // getNonBlocking method coerces the value to the config key's type.
+            Maybe<?> maybeValue = configInternal.getNonBlocking(configKey);
+            if (maybeValue.isPresent()) {
+                Object value = maybeValue.get();
                 try {
+                    // Cast is safe because the author of the constraint on the config key had to
+                    // keep its type to Predicte<? super T>, where T is ConfigKey<T>.
                     Predicate<Object> po = (Predicate<Object>) configKey.getConstraint();
                     boolean isValid;
                     if (po instanceof BrooklynObjectPredicate) {
@@ -125,7 +131,7 @@ public abstract class ConfigConstraints<T extends BrooklynObject> {
                         violating.add(configKey);
                     }
                 } catch (Exception e) {
-                    LOG.debug("Error checking constraint on {} {} ", configKey.getName(), e);
+                    LOG.debug("Error checking constraint on " + configKey.getName(), e);
                 }
             }
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/aa5c00f6/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
index 1291587..a2e12f4 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
@@ -1214,7 +1214,11 @@ public abstract class AbstractEntity extends AbstractBrooklynObject implements E
             
             getManagementSupport().getEntityChangeListener().onConfigChanged(key);
             return result;
+        }
 
+        @Override
+        protected ExecutionContext getContext() {
+            return AbstractEntity.this.getExecutionContext();
         }
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/aa5c00f6/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java b/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java
index 1f6788c..0fd3f7b 100644
--- a/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java
+++ b/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java
@@ -33,6 +33,7 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.Group;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.api.mgmt.ExecutionContext;
 import org.apache.brooklyn.api.mgmt.SubscriptionContext;
 import org.apache.brooklyn.api.mgmt.SubscriptionHandle;
 import org.apache.brooklyn.api.mgmt.Task;
@@ -473,6 +474,11 @@ public abstract class AbstractLocation extends AbstractBrooklynObject implements
         private ConfigInheritance getDefaultInheritance() {
             return ConfigInheritance.ALWAYS;
         }
+
+        @Override
+        protected ExecutionContext getContext() {
+            return AbstractLocation.this.getManagementContext().getServerExecutionContext();
+        }
     }
     
     public class BasicSubscriptionSupport implements SubscriptionSupportInternal {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/aa5c00f6/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java
index 4caa7e6..6cb8bb4 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java
@@ -19,9 +19,17 @@
 
 package org.apache.brooklyn.core.objs;
 
+import java.util.concurrent.TimeUnit;
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.api.mgmt.ExecutionContext;
 import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
+import org.apache.brooklyn.util.core.flags.TypeCoercions;
+import org.apache.brooklyn.util.core.task.Tasks;
 import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.time.Duration;
 
 public abstract class AbstractConfigurationSupportInternal implements BrooklynObjectInternal.ConfigurationSupportInternal {
 
@@ -41,6 +49,30 @@ public abstract class AbstractConfigurationSupportInternal implements BrooklynOb
     }
 
     @Override
+    public <T> Maybe<T> getNonBlocking(HasConfigKey<T> key) {
+        return getNonBlocking(key.getConfigKey());
+    }
+
+    @Override
+    public <T> Maybe<T> getNonBlocking(ConfigKey<T> key) {
+        // getRaw returns Maybe(val) if the key was explicitly set (where val can be null)
+        // or Absent if the config key was unset.
+        Object unresolved = getRaw(key).or(key.getDefaultValue());
+        final Object marker = new Object();
+        // Give tasks a short grace period to resolve.
+        Object resolved = Tasks.resolving(unresolved)
+                .as(Object.class)
+                .defaultValue(marker)
+                .timeout(Duration.of(5, TimeUnit.MILLISECONDS))
+                .context(getContext())
+                .swallowExceptions()
+                .get();
+        return (resolved != marker)
+               ? TypeCoercions.tryCoerce(resolved, key.getTypeToken())
+               : Maybe.<T>absent();
+    }
+
+    @Override
     public <T> T set(HasConfigKey<T> key, Task<T> val) {
         return set(key.getConfigKey(), val);
     }
@@ -50,4 +82,9 @@ public abstract class AbstractConfigurationSupportInternal implements BrooklynOb
         return set(key.getConfigKey(), val);
     }
 
+    /**
+     * @return An execution context for use by {@link #getNonBlocking(ConfigKey)}
+     */
+    @Nullable
+    protected abstract ExecutionContext getContext();
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/aa5c00f6/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
index 25c58ad..a7b00f4 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
@@ -357,6 +357,11 @@ public abstract class AbstractEntityAdjunct extends AbstractBrooklynObject imple
         public void refreshInheritedConfigOfChildren() {
             // no-op for location
         }
+
+        @Override
+        protected ExecutionContext getContext() {
+            return AbstractEntityAdjunct.this.execution;
+        }
     }
 
     public <T> T getConfig(ConfigKey<T> key) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/aa5c00f6/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java b/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java
index 257da5a..6888c52 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java
@@ -46,11 +46,11 @@ public interface BrooklynObjectInternal extends BrooklynObject, Rebindable {
 
     @Beta
     public interface ConfigurationSupportInternal extends Configurable.ConfigurationSupport {
-        
+
         /**
-         * Returns a read-only view of all the config key/value pairs on this entity, backed by a string-based map, 
+         * Returns a read-only view of all the config key/value pairs on this entity, backed by a string-based map,
          * including config names that did not match anything on this entity.
-         * 
+         *
          * TODO This method gives no information about which config is inherited versus local;
          * this means {@link ConfigKey#getInheritance()} cannot be respected. This is an unsolvable problem
          * for "config names that did not match anything on this entity". Therefore consider using
@@ -58,14 +58,14 @@ public interface BrooklynObjectInternal extends BrooklynObject, Rebindable {
          */
         @Beta
         ConfigBag getBag();
-        
+
         /**
-         * Returns a read-only view of the local (i.e. not inherited) config key/value pairs on this entity, 
+         * Returns a read-only view of the local (i.e. not inherited) config key/value pairs on this entity,
          * backed by a string-based map, including config names that did not match anything on this entity.
          */
         @Beta
         ConfigBag getLocalBag();
-        
+
         /**
          * Returns the uncoerced value for this config key, if available, not taking any default.
          * If there is no local value and there is an explicit inherited value, will return the inherited.
@@ -92,6 +92,20 @@ public interface BrooklynObjectInternal extends BrooklynObject, Rebindable {
         @Beta
         Maybe<Object> getLocalRaw(HasConfigKey<?> key);
 
+        /**
+         * Attempts to coerce the value for this config key, if available,
+         * taking a default and {@link Maybe#absent absent} if the uncoerced
+         * cannot be resolved within a short timeframe.
+         */
+        @Beta
+        <T> Maybe<T> getNonBlocking(ConfigKey<T> key);
+
+        /**
+         * @see {@link #getNonBlocking(ConfigKey)}
+         */
+        @Beta
+        <T> Maybe<T> getNonBlocking(HasConfigKey<T> key);
+
         @Beta
         void addToLocalBag(Map<String, ?> vals);
 
@@ -100,7 +114,7 @@ public interface BrooklynObjectInternal extends BrooklynObject, Rebindable {
 
         @Beta
         void refreshInheritedConfig();
-        
+
         @Beta
         void refreshInheritedConfigOfChildren();
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/aa5c00f6/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java b/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
index 05e0814..8a9e1c6 100644
--- a/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
@@ -23,8 +23,11 @@ import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.fail;
 
+import java.util.concurrent.Callable;
+
 import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.api.mgmt.Task;
 import org.apache.brooklyn.api.objs.BrooklynObject;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
@@ -38,8 +41,11 @@ import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
 import org.apache.brooklyn.core.test.entity.TestEntity;
 import org.apache.brooklyn.core.test.entity.TestEntityImpl;
 import org.apache.brooklyn.core.test.policy.TestPolicy;
+import org.apache.brooklyn.util.core.task.Tasks;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.net.Networking;
+import org.apache.brooklyn.util.time.Duration;
+import org.apache.brooklyn.util.time.Time;
 import org.testng.annotations.Test;
 
 import com.google.common.base.Predicate;
@@ -164,7 +170,7 @@ public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
             fail("Expected exception when managing entity setting invalid default value");
         } catch (Exception e) {
             Throwable t = Exceptions.getFirstThrowableOfType(e, ConstraintViolationException.class);
-            assertNotNull(t);
+            assertNotNull(t, "Original exception was: " + Exceptions.collapseText(e));
         }
     }
 
@@ -176,7 +182,7 @@ public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
             fail("Expected exception when config key set to null");
         } catch (Exception e) {
             Throwable t = Exceptions.getFirstThrowableOfType(e, ConstraintViolationException.class);
-            assertNotNull(t);
+            assertNotNull(t, "Original exception was: " + Exceptions.collapseText(e));
         }
     }
 
@@ -188,7 +194,7 @@ public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
             fail("Expected exception when managing entity with invalid config");
         } catch (Exception e) {
             Throwable t = Exceptions.getFirstThrowableOfType(e, ConstraintViolationException.class);
-            assertNotNull(t);
+            assertNotNull(t, "Original exception was: " + Exceptions.collapseText(e));
         }
     }
 
@@ -196,14 +202,14 @@ public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
     public void testExceptionWhenAppGrandchildHasInvalidConfig() {
         app.start(ImmutableList.of(app.newSimulatedLocation()));
         TestEntity testEntity = app.addChild(EntitySpec.create(TestEntity.class));
+        try {
         testEntity.addChild(EntitySpec.create(EntityRequiringConfigKeyInRange.class)
                 .configure(EntityRequiringConfigKeyInRange.RANGE, -1));
-        try {
             Entities.manage(testEntity);
             fail("Expected exception when managing child with invalid config");
         } catch (Exception e) {
             Throwable t = Exceptions.getFirstThrowableOfType(e, ConstraintViolationException.class);
-            assertNotNull(t);
+            assertNotNull(t, "Original exception was: " + Exceptions.collapseText(e));
         }
     }
 
@@ -217,7 +223,7 @@ public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
             fail("Expected exception when validating policy with missing config");
         } catch (Exception e) {
             Throwable t = Exceptions.getFirstThrowableOfType(e, ConstraintViolationException.class);
-            assertNotNull(t);
+            assertNotNull(t, "Original exception was: " + Exceptions.collapseText(e));
         }
     }
 
@@ -229,7 +235,7 @@ public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
             fail("Expected exception when creating policy with missing config");
         } catch (Exception e) {
             Throwable t = Exceptions.getFirstThrowableOfType(e, ConstraintViolationException.class);
-            assertNotNull(t);
+            assertNotNull(t, "Original exception was: " + Exceptions.collapseText(e));
         }
     }
 
@@ -241,7 +247,7 @@ public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
             fail("Expected exception when creating enricher with invalid config");
         } catch (Exception e) {
             Throwable t = Exceptions.getFirstThrowableOfType(e, ConstraintViolationException.class);
-            assertNotNull(t, "Exception was: " + Exceptions.collapseText(e));
+            assertNotNull(t, "Original exception was: " + Exceptions.collapseText(e));
         }
     }
 
@@ -278,8 +284,39 @@ public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
             fail("Expected exception when managing entity with incorrect config");
         } catch (Exception e) {
             Throwable t = Exceptions.getFirstThrowableOfType(e, ConstraintViolationException.class);
-            assertNotNull(t);
+            assertNotNull(t, "Original exception was: " + Exceptions.collapseText(e));
         }
     }
 
+    @Test
+    public void testQuickFutureResolved() {
+        // Result of task is -1, outside of the range specified by the config key.
+        try {
+            app.createAndManageChild(EntitySpec.create(EntityRequiringConfigKeyInRange.class)
+                    .configure(EntityRequiringConfigKeyInRange.RANGE, sleepingTask(Duration.ZERO, -1)));
+            fail("Expected exception when managing entity with incorrect config");
+        } catch (Exception e) {
+            Throwable t = Exceptions.getFirstThrowableOfType(e, ConstraintViolationException.class);
+            assertNotNull(t, "Original exception was: " + Exceptions.collapseText(e));
+        }
+    }
+
+    @Test
+    public void testSlowFutureNotResolved() {
+        // i.e. no exception because task is too slow to resolve.
+        app.createAndManageChild(EntitySpec.create(EntityRequiringConfigKeyInRange.class)
+                .configure(EntityRequiringConfigKeyInRange.RANGE, sleepingTask(Duration.PRACTICALLY_FOREVER, -1)));
+    }
+
+    private static <T> Task<T> sleepingTask(final Duration delay, final T result) {
+        return Tasks.<T>builder()
+                .body(new Callable<T>() {
+                    @Override public T call() throws Exception {
+                        Time.sleep(delay);
+                        return result;
+                    }
+                })
+                .build();
+    }
+
 }


[08/17] incubator-brooklyn git commit: AbstractBrooklynRestResource.RestValueResolver uses timeout

Posted by sj...@apache.org.
AbstractBrooklynRestResource.RestValueResolver uses timeout


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/5423137e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/5423137e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/5423137e

Branch: refs/heads/master
Commit: 5423137e3f9f925acf54dfc98d39d3fd68d1fd6e
Parents: 781e2cf
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Thu Oct 8 11:58:52 2015 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Thu Oct 8 17:54:43 2015 +0100

----------------------------------------------------------------------
 .../rest/resources/AbstractBrooklynRestResource.java    | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5423137e/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/AbstractBrooklynRestResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/AbstractBrooklynRestResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/AbstractBrooklynRestResource.java
index 9c24707..a07e684 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/AbstractBrooklynRestResource.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/AbstractBrooklynRestResource.java
@@ -126,7 +126,7 @@ public abstract class AbstractBrooklynRestResource implements ManagementContextI
         public RestValueResolver renderAs(Object rendererHintSource) { this.rendererHintSource = rendererHintSource; return this; }
 
         public Object resolve() {
-            Object valueResult = getImmediateValue(valueToResolve, entity);
+            Object valueResult = getImmediateValue(valueToResolve, entity, timeout);
             if (valueResult==UNRESOLVED) valueResult = valueToResolve;
             if (rendererHintSource!=null && Boolean.FALSE.equals(raw)) {
                 valueResult = RendererHints.applyDisplayValueHintUnchecked(rendererHintSource, valueResult);
@@ -136,8 +136,14 @@ public abstract class AbstractBrooklynRestResource implements ManagementContextI
         
         private static Object UNRESOLVED = "UNRESOLVED".toCharArray();
         
-        private static Object getImmediateValue(Object value, @Nullable Entity context) {
-            return Tasks.resolving(value).as(Object.class).defaultValue(UNRESOLVED).timeout(Duration.ZERO).context(context).swallowExceptions().get();
+        private static Object getImmediateValue(Object value, @Nullable Entity context, @Nullable Duration timeout) {
+            return Tasks.resolving(value)
+                    .as(Object.class)
+                    .defaultValue(UNRESOLVED)
+                    .timeout(timeout)
+                    .context(context)
+                    .swallowExceptions()
+                    .get();
         }
 
     }


[17/17] incubator-brooklyn git commit: This closes #951

Posted by sj...@apache.org.
This closes #951


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/2a004d93
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/2a004d93
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/2a004d93

Branch: refs/heads/master
Commit: 2a004d93922246e87e500e7a70d37df302727dc3
Parents: 8e4b3ba ee7b43b
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Wed Oct 14 14:51:24 2015 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Wed Oct 14 14:51:24 2015 +0100

----------------------------------------------------------------------
 docs/guide/ops/high-availability.md | 5 +++++
 1 file changed, 5 insertions(+)
----------------------------------------------------------------------



[09/17] incubator-brooklyn git commit: Eliminate duplication between implementations of ConfigurationSupportInternal

Posted by sj...@apache.org.
Eliminate duplication between implementations of ConfigurationSupportInternal


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/2f9b9e1f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/2f9b9e1f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/2f9b9e1f

Branch: refs/heads/master
Commit: 2f9b9e1f3ca33a5b933b0dac0110a29b7a61ff25
Parents: 5423137
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Thu Oct 8 12:25:27 2015 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Thu Oct 8 17:54:43 2015 +0100

----------------------------------------------------------------------
 .../brooklyn/core/entity/AbstractEntity.java    | 28 +----------
 .../core/location/AbstractLocation.java         | 29 +----------
 .../AbstractConfigurationSupportInternal.java   | 53 ++++++++++++++++++++
 .../core/objs/AbstractEntityAdjunct.java        | 28 +----------
 .../core/objs/BrooklynObjectInternal.java       |  4 +-
 5 files changed, 60 insertions(+), 82 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2f9b9e1f/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
index eb919a7..1291587 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
@@ -77,6 +77,7 @@ import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
 import org.apache.brooklyn.core.mgmt.internal.SubscriptionTracker;
 import org.apache.brooklyn.core.mgmt.rebind.BasicEntityRebindSupport;
 import org.apache.brooklyn.core.objs.AbstractBrooklynObject;
+import org.apache.brooklyn.core.objs.AbstractConfigurationSupportInternal;
 import org.apache.brooklyn.core.objs.AbstractEntityAdjunct;
 import org.apache.brooklyn.core.objs.AbstractEntityAdjunct.AdjunctTagSupport;
 import org.apache.brooklyn.core.policy.AbstractPolicy;
@@ -1134,7 +1135,7 @@ public abstract class AbstractEntity extends AbstractBrooklynObject implements E
      */
     @Beta
     // TODO revert to private when config() is reverted to return ConfigurationSupportInternal
-    public class BasicConfigurationSupport implements ConfigurationSupportInternal {
+    public class BasicConfigurationSupport extends AbstractConfigurationSupportInternal {
 
         @Override
         public <T> T get(ConfigKey<T> key) {
@@ -1142,31 +1143,16 @@ public abstract class AbstractEntity extends AbstractBrooklynObject implements E
         }
 
         @Override
-        public <T> T get(HasConfigKey<T> key) {
-            return get(key.getConfigKey());
-        }
-
-        @Override
         public <T> T set(ConfigKey<T> key, T val) {
             return setConfigInternal(key, val);
         }
 
         @Override
-        public <T> T set(HasConfigKey<T> key, T val) {
-            return set(key.getConfigKey(), val);
-        }
-
-        @Override
         public <T> T set(ConfigKey<T> key, Task<T> val) {
             return setConfigInternal(key, val);
         }
 
         @Override
-        public <T> T set(HasConfigKey<T> key, Task<T> val) {
-            return set(key.getConfigKey(), val);
-        }
-
-        @Override
         public ConfigBag getBag() {
             return configsInternal.getAllConfigBag();
         }
@@ -1182,21 +1168,11 @@ public abstract class AbstractEntity extends AbstractBrooklynObject implements E
         }
 
         @Override
-        public Maybe<Object> getRaw(HasConfigKey<?> key) {
-            return getRaw(key.getConfigKey());
-        }
-
-        @Override
         public Maybe<Object> getLocalRaw(ConfigKey<?> key) {
             return configsInternal.getConfigRaw(key, false);
         }
 
         @Override
-        public Maybe<Object> getLocalRaw(HasConfigKey<?> key) {
-            return getLocalRaw(key.getConfigKey());
-        }
-
-        @Override
         public void addToLocalBag(Map<String, ?> vals) {
             configsInternal.addToLocalBag(vals);
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2f9b9e1f/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java b/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java
index 26e58e1..1f6788c 100644
--- a/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java
+++ b/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java
@@ -59,6 +59,7 @@ import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
 import org.apache.brooklyn.core.mgmt.internal.SubscriptionTracker;
 import org.apache.brooklyn.core.mgmt.rebind.BasicLocationRebindSupport;
 import org.apache.brooklyn.core.objs.AbstractBrooklynObject;
+import org.apache.brooklyn.core.objs.AbstractConfigurationSupportInternal;
 import org.apache.brooklyn.util.collections.SetFromLiveMap;
 import org.apache.brooklyn.util.core.config.ConfigBag;
 import org.apache.brooklyn.util.core.flags.FlagUtils;
@@ -379,7 +380,7 @@ public abstract class AbstractLocation extends AbstractBrooklynObject implements
         return subscriptions;
     }
 
-    private class BasicConfigurationSupport implements ConfigurationSupportInternal {
+    private class BasicConfigurationSupport extends AbstractConfigurationSupportInternal {
 
         @Override
         public <T> T get(ConfigKey<T> key) {
@@ -397,11 +398,6 @@ public abstract class AbstractLocation extends AbstractBrooklynObject implements
         }
 
         @Override
-        public <T> T get(HasConfigKey<T> key) {
-            return get(key.getConfigKey());
-        }
-
-        @Override
         public <T> T set(ConfigKey<T> key, T val) {
             T result = configBag.put(key, val);
             onChanged();
@@ -409,23 +405,12 @@ public abstract class AbstractLocation extends AbstractBrooklynObject implements
         }
 
         @Override
-        public <T> T set(HasConfigKey<T> key, T val) {
-            return set(key.getConfigKey(), val);
-        }
-
-        @Override
         public <T> T set(ConfigKey<T> key, Task<T> val) {
             // TODO Support for locations
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public <T> T set(HasConfigKey<T> key, Task<T> val) {
-            // TODO Support for locations
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
         public ConfigBag getBag() {
             ConfigBag result = ConfigBag.newInstanceExtending(configBag, ImmutableMap.of());
             Location p = getParent();
@@ -446,22 +431,12 @@ public abstract class AbstractLocation extends AbstractBrooklynObject implements
         }
 
         @Override
-        public Maybe<Object> getRaw(HasConfigKey<?> key) {
-            return getRaw(key.getConfigKey());
-        }
-
-        @Override
         public Maybe<Object> getLocalRaw(ConfigKey<?> key) {
             if (hasConfig(key, false)) return Maybe.of(getLocalBag().getStringKey(key.getName()));
             return Maybe.absent();
         }
 
         @Override
-        public Maybe<Object> getLocalRaw(HasConfigKey<?> key) {
-            return getLocalRaw(key.getConfigKey());
-        }
-
-        @Override
         public void addToLocalBag(Map<String, ?> vals) {
             configBag.putAll(vals);
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2f9b9e1f/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java
new file mode 100644
index 0000000..4caa7e6
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java
@@ -0,0 +1,53 @@
+/*
+ * 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.core.objs;
+
+import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
+import org.apache.brooklyn.util.guava.Maybe;
+
+public abstract class AbstractConfigurationSupportInternal implements BrooklynObjectInternal.ConfigurationSupportInternal {
+
+    @Override
+    public <T> T get(HasConfigKey<T> key) {
+        return get(key.getConfigKey());
+    }
+
+    @Override
+    public Maybe<Object> getLocalRaw(HasConfigKey<?> key) {
+        return getLocalRaw(key.getConfigKey());
+    }
+
+    @Override
+    public Maybe<Object> getRaw(HasConfigKey<?> key) {
+        return getRaw(key.getConfigKey());
+    }
+
+    @Override
+    public <T> T set(HasConfigKey<T> key, Task<T> val) {
+        return set(key.getConfigKey(), val);
+    }
+
+    @Override
+    public <T> T set(HasConfigKey<T> key, T val) {
+        return set(key.getConfigKey(), val);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2f9b9e1f/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
index 3434471..25c58ad 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
@@ -41,7 +41,6 @@ import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.api.sensor.Sensor;
 import org.apache.brooklyn.api.sensor.SensorEventListener;
 import org.apache.brooklyn.config.ConfigKey;
-import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
 import org.apache.brooklyn.config.ConfigMap;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.enricher.AbstractEnricher;
@@ -289,18 +288,13 @@ public abstract class AbstractEntityAdjunct extends AbstractBrooklynObject imple
         }
     }
     
-    private class BasicConfigurationSupport implements ConfigurationSupportInternal {
+    private class BasicConfigurationSupport extends AbstractConfigurationSupportInternal {
 
         @Override
         public <T> T get(ConfigKey<T> key) {
             return configsInternal.getConfig(key);
         }
 
-        @Override
-        public <T> T get(HasConfigKey<T> key) {
-            return get(key.getConfigKey());
-        }
-
         @SuppressWarnings("unchecked")
         @Override
         public <T> T set(ConfigKey<T> key, T val) {
@@ -312,11 +306,6 @@ public abstract class AbstractEntityAdjunct extends AbstractBrooklynObject imple
             return result;
         }
 
-        @Override
-        public <T> T set(HasConfigKey<T> key, T val) {
-            return setConfig(key.getConfigKey(), val);
-        }
-
         @SuppressWarnings("unchecked")
         @Override
         public <T> T set(ConfigKey<T> key, Task<T> val) {
@@ -330,11 +319,6 @@ public abstract class AbstractEntityAdjunct extends AbstractBrooklynObject imple
         }
 
         @Override
-        public <T> T set(HasConfigKey<T> key, Task<T> val) {
-            return set(key.getConfigKey(), val);
-        }
-
-        @Override
         public ConfigBag getBag() {
             return getLocalBag();
         }
@@ -350,21 +334,11 @@ public abstract class AbstractEntityAdjunct extends AbstractBrooklynObject imple
         }
 
         @Override
-        public Maybe<Object> getRaw(HasConfigKey<?> key) {
-            return getRaw(key.getConfigKey());
-        }
-
-        @Override
         public Maybe<Object> getLocalRaw(ConfigKey<?> key) {
             return configsInternal.getConfigRaw(key, false);
         }
 
         @Override
-        public Maybe<Object> getLocalRaw(HasConfigKey<?> key) {
-            return getLocalRaw(key.getConfigKey());
-        }
-
-        @Override
         public void addToLocalBag(Map<String, ?> vals) {
             configsInternal.addToLocalBag(vals);
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2f9b9e1f/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java b/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java
index d076f4e..257da5a 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java
@@ -74,7 +74,7 @@ public interface BrooklynObjectInternal extends BrooklynObject, Rebindable {
         Maybe<Object> getRaw(ConfigKey<?> key);
 
         /**
-         * @see {@link #getConfigRaw(ConfigKey)}
+         * @see {@link #getRaw(ConfigKey)}
          */
         @Beta
         Maybe<Object> getRaw(HasConfigKey<?> key);
@@ -87,7 +87,7 @@ public interface BrooklynObjectInternal extends BrooklynObject, Rebindable {
         Maybe<Object> getLocalRaw(ConfigKey<?> key);
 
         /**
-         * @see {@link #getLocalConfigRaw(ConfigKey)}
+         * @see {@link #getLocalRaw(ConfigKey)}
          */
         @Beta
         Maybe<Object> getLocalRaw(HasConfigKey<?> key);


[02/17] incubator-brooklyn git commit: Constraints on ConfigKeys for entities

Posted by sj...@apache.org.
Constraints on ConfigKeys for entities

And their validation when an entity is first managed.


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/87b4b9b2
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/87b4b9b2
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/87b4b9b2

Branch: refs/heads/master
Commit: 87b4b9b21f3427ef36bc3fc31f5bff9261782e84
Parents: f0e0ba7
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Wed Aug 26 10:58:07 2015 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Thu Oct 8 11:10:31 2015 +0100

----------------------------------------------------------------------
 .../brooklyn/core/config/BasicConfigKey.java    |  20 ++-
 .../brooklyn/core/config/ConfigConstraints.java |  90 +++++++++++
 .../core/mgmt/internal/LocalEntityManager.java  |   3 +
 .../core/config/ConfigKeyConstraintTest.java    | 148 +++++++++++++++++++
 .../org/apache/brooklyn/config/ConfigKey.java   |  12 +-
 5 files changed, 270 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/87b4b9b2/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java b/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
index 069936c..239e7dd 100644
--- a/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
+++ b/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
@@ -25,6 +25,7 @@ import java.util.Collection;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
 
+import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
 import org.apache.brooklyn.api.mgmt.ExecutionContext;
@@ -40,6 +41,8 @@ import org.slf4j.LoggerFactory;
 import com.google.common.annotations.Beta;
 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.base.Splitter;
 import com.google.common.collect.Lists;
 import com.google.common.reflect.TypeToken;
@@ -75,7 +78,8 @@ public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializab
             .description(key.getDescription())
             .defaultValue(key.getDefaultValue())
             .reconfigurable(key.isReconfigurable())
-            .inheritance(key.getInheritance());
+            .inheritance(key.getInheritance())
+            .constraint(key.getConstraint());
     }
 
     public static class Builder<T> {
@@ -84,6 +88,7 @@ public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializab
         private String description;
         private T defaultValue;
         private boolean reconfigurable;
+        private Predicate<? super T> constraint;
         private ConfigInheritance inheritance;
         
         public Builder<T> name(String val) {
@@ -107,6 +112,9 @@ public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializab
         public Builder<T> inheritance(ConfigInheritance val) {
             this.inheritance = val; return this;
         }
+        public Builder<T> constraint(Predicate<? super T> constraint) {
+            this.constraint = checkNotNull(constraint, "constraint"); return this;
+        }
         public BasicConfigKey<T> build() {
             return new BasicConfigKey<T>(this);
         }
@@ -119,6 +127,7 @@ public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializab
     private T defaultValue;
     private boolean reconfigurable;
     private ConfigInheritance inheritance;
+    private Predicate<? super T> constraint;
 
     // FIXME In groovy, fields were `public final` with a default constructor; do we need the gson?
     public BasicConfigKey() { /* for gson */ }
@@ -152,6 +161,7 @@ public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializab
         
         this.defaultValue = defaultValue;
         this.reconfigurable = false;
+        this.constraint = Predicates.alwaysTrue();
     }
 
     protected BasicConfigKey(Builder<T> builder) {
@@ -162,6 +172,7 @@ public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializab
         this.defaultValue = builder.defaultValue;
         this.reconfigurable = builder.reconfigurable;
         this.inheritance = builder.inheritance;
+        this.constraint = builder.constraint;
     }
     
     /** @see ConfigKey#getName() */
@@ -196,7 +207,12 @@ public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializab
     public ConfigInheritance getInheritance() {
         return inheritance;
     }
-    
+
+    @Override @Nonnull
+    public Predicate<? super T> getConstraint() {
+        return constraint;
+    }
+
     /** @see ConfigKey#getNameParts() */
     @Override public Collection<String> getNameParts() {
         return Lists.newArrayList(dots.split(name));

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/87b4b9b2/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java b/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
new file mode 100644
index 0000000..0ac030f
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
@@ -0,0 +1,90 @@
+/*
+ * 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.core.config;
+
+import java.util.List;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.entity.EntityInternal;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+public class ConfigConstraints {
+
+    public static final Logger LOG = LoggerFactory.getLogger(ConfigConstraints.class);
+    private final Entity entity;
+
+    public ConfigConstraints(Entity e) {
+        this.entity = e;
+    }
+
+    /**
+     * Checks all constraints of all config keys available to an entity.
+     */
+    public static void assertValid(Entity e) {
+        Iterable<ConfigKey<?>> violations = new ConfigConstraints(e).getViolations();
+        if (!Iterables.isEmpty(violations)) {
+            throw new AssertionError("ConfigKeys violate constraints: " + violations);
+        }
+    }
+
+    public boolean isValid() {
+        return Iterables.isEmpty(getViolations());
+    }
+
+    public Iterable<ConfigKey<?>> getViolations() {
+        return validateAll();
+    }
+
+    @SuppressWarnings("unchecked")
+    private Iterable<ConfigKey<?>> validateAll() {
+        EntityInternal ei = (EntityInternal) entity;
+        List<ConfigKey<?>> violating = Lists.newArrayList();
+
+        for (ConfigKey<?> configKey : getEntityConfigKeys(entity)) {
+            // getRaw returns null if explicitly set and absent if config key was unset.
+            Object value = ei.config().getRaw(configKey).or(configKey.getDefaultValue());
+
+            if (value == null || value.getClass().isAssignableFrom(configKey.getType())) {
+                // Cast should be safe because the author of the constraint on the config key had to
+                // keep its type to Predicte<? super T>, where T is ConfigKey<T>.
+                try {
+                    Predicate<Object> po = (Predicate<Object>) configKey.getConstraint();
+                    if (!po.apply(value)) {
+                        violating.add(configKey);
+                    }
+                } catch (Exception e) {
+                    LOG.debug("Error checking constraint on {} {} ", configKey.getName(), e);
+                }
+            }
+        }
+        return violating;
+    }
+
+    private static Iterable<ConfigKey<?>> getEntityConfigKeys(Entity entity) {
+        return entity.getEntityType().getConfigKeys();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/87b4b9b2/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalEntityManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalEntityManager.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalEntityManager.java
index 8589eb1..f1fde20 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalEntityManager.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalEntityManager.java
@@ -44,6 +44,7 @@ import org.apache.brooklyn.api.policy.PolicySpec;
 import org.apache.brooklyn.api.sensor.Enricher;
 import org.apache.brooklyn.api.sensor.EnricherSpec;
 import org.apache.brooklyn.core.BrooklynLogging;
+import org.apache.brooklyn.core.config.ConfigConstraints;
 import org.apache.brooklyn.core.entity.AbstractEntity;
 import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.entity.EntityInternal;
@@ -268,6 +269,8 @@ public class LocalEntityManager implements EntityManagerInternal {
                     new Exception("source of duplicate management of "+e));
             return;
         }
+        ConfigConstraints.assertValid(e);
+
         manageRecursive(e, ManagementTransitionMode.guessing(BrooklynObjectManagementMode.NONEXISTENT, BrooklynObjectManagementMode.MANAGED_PRIMARY));
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/87b4b9b2/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java b/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
new file mode 100644
index 0000000..130e3a7
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
@@ -0,0 +1,148 @@
+/*
+ * 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.core.config;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.fail;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.core.test.entity.TestEntityImpl;
+import org.apache.brooklyn.core.test.policy.TestPolicy;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Range;
+
+public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
+
+    @ImplementedBy(EntityWithNonNullConstraintImpl.class)
+    public static interface EntityWithNonNullConstraint extends TestEntity {
+        ConfigKey<Object> NON_NULL_CONFIG = ConfigKeys.builder(Object.class)
+                .name("test.conf.non-null.without-default")
+                .description("Configuration key that must not be null")
+                .constraint(Predicates.notNull())
+                .build();
+
+    }
+
+    @ImplementedBy(EntityWithNonNullConstraintWithNonNullDefaultImpl.class)
+    public static interface EntityWithNonNullConstraintWithNonNullDefault extends TestEntity {
+        ConfigKey<Object> NON_NULL_WITH_DEFAULT = ConfigKeys.builder(Object.class)
+                .name("test.conf.non-null.with-default")
+                .description("Configuration key that must not be null")
+                .defaultValue(new Object())
+                .constraint(Predicates.notNull())
+                .build();
+    }
+
+    @ImplementedBy(EntityRequiringConfigKeyInRangeImpl.class)
+    public static interface EntityRequiringConfigKeyInRange extends TestEntity {
+        ConfigKey<Integer> RANGE = ConfigKeys.builder(Integer.class)
+                .name("test.conf.range")
+                .description("Configuration key that must not be between zero and nine")
+                .defaultValue(0)
+                .constraint(Range.closed(0, 9))
+                .build();
+    }
+
+    @ImplementedBy(EntityProvidingDefaultValueForConfigKeyInRangeImpl.class)
+    public static interface EntityProvidingDefaultValueForConfigKeyInRange extends EntityRequiringConfigKeyInRange {
+        ConfigKey<Integer> REVISED_RANGE = ConfigKeys.newConfigKeyWithDefault(RANGE, -1);
+    }
+
+    public static class EntityWithNonNullConstraintImpl extends TestEntityImpl implements EntityWithNonNullConstraint {
+    }
+
+    public static class EntityWithNonNullConstraintWithNonNullDefaultImpl extends TestEntityImpl implements EntityWithNonNullConstraintWithNonNullDefault {
+    }
+
+    public static class EntityRequiringConfigKeyInRangeImpl extends TestEntityImpl implements EntityRequiringConfigKeyInRange {
+    }
+
+    public static class EntityProvidingDefaultValueForConfigKeyInRangeImpl extends TestEntityImpl implements EntityProvidingDefaultValueForConfigKeyInRange {
+    }
+
+    @Test
+    public void testExceptionWhenEntityHasNullConfig() {
+        try {
+            app.createAndManageChild(EntitySpec.create(EntityWithNonNullConstraint.class));
+            fail("Expected exception when managing entity with missing config");
+        } catch (Exception e) {
+            Throwable t = Exceptions.getFirstThrowableOfType(e, AssertionError.class);
+            assertNotNull(t);
+        }
+    }
+
+    @Test
+    public void testNoExceptionWhenEntityHasValueForRequiredConfig() {
+        app.createAndManageChild(EntitySpec.create(EntityWithNonNullConstraint.class)
+                .configure(EntityWithNonNullConstraint.NON_NULL_CONFIG, new Object()));
+    }
+
+    @Test
+    public void testNoExceptionWhenDefaultValueIsValid() {
+        app.createAndManageChild(EntitySpec.create(EntityRequiringConfigKeyInRange.class));
+    }
+
+    @Test
+    public void testExceptionWhenSubclassSetsInvalidDefaultValue() {
+        try {
+            app.createAndManageChild(EntitySpec.create(EntityProvidingDefaultValueForConfigKeyInRange.class));
+            fail("Expected exception when managing entity setting invalid default value");
+        } catch (Exception e) {
+            Throwable t = Exceptions.getFirstThrowableOfType(e, AssertionError.class);
+            assertNotNull(t);
+        }
+    }
+
+    @Test
+    public void testExceptionIsThrownWhenUserSetsNullValueToConfigWithNonNullDefault() {
+        try {
+            app.createAndManageChild(EntitySpec.create(EntityWithNonNullConstraintWithNonNullDefault.class)
+                    .configure(EntityWithNonNullConstraintWithNonNullDefault.NON_NULL_WITH_DEFAULT, (Object) null));
+            fail("Expected exception when config key set to null");
+        } catch (Exception e) {
+            Throwable t = Exceptions.getFirstThrowableOfType(e, AssertionError.class);
+            assertNotNull(t);
+        }
+    }
+
+    @Test(enabled = false)
+    public void testExceptionWhenPolicyHasNullConfig() {
+        app.createAndManageChild(EntitySpec.create(TestEntity.class)
+                .policy(PolicySpec.create(TestPolicy.class)
+                        .configure(EntityWithNonNullConstraint.NON_NULL_CONFIG, (Object) null)));
+        try {
+            app.start(ImmutableList.of(app.newSimulatedLocation()));
+            fail("Expected exception when starting entity with policy with missing config");
+        } catch (Exception e) {
+            Throwable t = Exceptions.getFirstThrowableOfType(e, AssertionError.class);
+            assertNotNull(t);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/87b4b9b2/utils/common/src/main/java/org/apache/brooklyn/config/ConfigKey.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/config/ConfigKey.java b/utils/common/src/main/java/org/apache/brooklyn/config/ConfigKey.java
index 3487bc4..9dab87d 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/config/ConfigKey.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/config/ConfigKey.java
@@ -20,8 +20,11 @@ package org.apache.brooklyn.config;
 
 import java.util.Collection;
 
+import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
+import com.google.common.annotations.Beta;
+import com.google.common.base.Predicate;
 import com.google.common.reflect.TypeToken;
 
 /**
@@ -79,12 +82,19 @@ public interface ConfigKey<T> {
      * @return True if the configuration can be changed at runtime.
      */
     boolean isReconfigurable();
-    
+
     /**
      * @return The inheritance model, or <code>null</code> for the default in any context.
      */
     @Nullable ConfigInheritance getInheritance();
 
+    /**
+     * @return the predicate constraining the key's value.
+     */
+    @Beta
+    @Nonnull
+    Predicate<? super T> getConstraint();
+
     /** Interface for elements which want to be treated as a config key without actually being one
      * (e.g. config attribute sensors).
      */


[11/17] incubator-brooklyn git commit: add hot backup description to docs

Posted by sj...@apache.org.
add hot backup description to docs


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

Branch: refs/heads/master
Commit: ee7b43b2963258b204c9c6b4819c880532142872
Parents: 49eabd1
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Tue Oct 13 10:28:58 2015 +0200
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Tue Oct 13 10:29:26 2015 +0200

----------------------------------------------------------------------
 docs/guide/ops/high-availability.md | 5 +++++
 1 file changed, 5 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ee7b43b2/docs/guide/ops/high-availability.md
----------------------------------------------------------------------
diff --git a/docs/guide/ops/high-availability.md b/docs/guide/ops/high-availability.md
index 2a2e512..5f05cca 100644
--- a/docs/guide/ops/high-availability.md
+++ b/docs/guide/ops/high-availability.md
@@ -21,6 +21,10 @@ Once one node is running as `MASTER`, other nodes start in either `STANDBY` or `
   As with the standby node, if a hot-standby node detects that the master fails,
   it will be a candidate for promotion to master.
 
+* In `HOT_BACKUP` mode, a Brooklyn instance will read and make available the live state of
+  entities, as a read-only copy. However this node is not able to become master,
+  so it can safely be used to test compatibility across different versions.
+
 To explicitly specify what HA mode a node should be in, the following CLI options are available
 for the parameter `--highAvailability`:
 
@@ -29,6 +33,7 @@ for the parameter `--highAvailability`:
 * `master`: will startup as master; if there is already a master then fails immediately
 * `standby`: will start up as lukewarm standby; if there is not already a master then fails immediately
 * `hot_standby`: will start up as hot standby; if there is not already a master then fails immediately
+* `hot_backup`: will start up as hot backup; this can be done even if there is not already a master; this node will not be a master 
 
 The REST API offers live detection and control of the HA mode,
 including setting priority to control which nodes will be promoted on master failure:


[05/17] incubator-brooklyn git commit: ResourcePredicates.urlExists()

Posted by sj...@apache.org.
ResourcePredicates.urlExists()

Adds a predicate that is true if a resource is available via a
configurable context object. ConfigConstraints looks for instances of the
Brooklyn-object-aware predicate and calls apply(T, BrooklynObject) if
possible.

Requires non-deployment management contexts to return useful entitlement
and OSGi managers.

Rework contextual predicates

Context must be passed to apply, not set as a field on the predicate.


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/60d85fa4
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/60d85fa4
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/60d85fa4

Branch: refs/heads/master
Commit: 60d85fa41f566785663c45948d7a01612d3e92d5
Parents: 7a728b4
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Fri Sep 11 17:14:17 2015 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Thu Oct 8 17:54:39 2015 +0100

----------------------------------------------------------------------
 .../brooklyn/core/config/ConfigConstraints.java | 17 ++++--
 .../NonDeploymentManagementContext.java         | 18 +++---
 .../core/objs/BrooklynObjectPredicate.java      | 33 +++++++++++
 .../brooklyn/util/core/ResourcePredicates.java  | 61 ++++++++++++++++++++
 .../core/config/ConfigKeyConstraintTest.java    | 55 +++++++++++++++---
 .../util/guava/PredicateWithContext.java        | 33 +++++++++++
 6 files changed, 197 insertions(+), 20 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/60d85fa4/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java b/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
index 24142a5..6c6445f 100644
--- a/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
+++ b/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
@@ -27,6 +27,7 @@ import org.apache.brooklyn.api.objs.BrooklynObject;
 import org.apache.brooklyn.api.objs.EntityAdjunct;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.objs.AbstractEntityAdjunct;
+import org.apache.brooklyn.core.objs.BrooklynObjectPredicate;
 import org.apache.brooklyn.core.objs.BrooklynObjectInternal;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -44,8 +45,8 @@ public abstract class ConfigConstraints<T extends BrooklynObject> {
     /**
      * Checks all constraints of all config keys available to an entity.
      * <p>
-     * If a constraint is a {@link BrooklynObjectAwarePredicate} then it will be
-     * informed of the entity before the predicate is tested.
+     * If a constraint is a {@link BrooklynObjectPredicate} then
+     * {@link BrooklynObjectPredicate#apply(Object, BrooklynObject)} will be used.
      */
     public static void assertValid(Entity entity) {
         Iterable<ConfigKey<?>> violations = new EntityConfigConstraints(entity).getViolations();
@@ -57,8 +58,8 @@ public abstract class ConfigConstraints<T extends BrooklynObject> {
     /**
      * Checks all constraints of all config keys available to an entity adjunct.
      * <p>
-     * If a constraint is a {@link BrooklynObjectAwarePredicate} then it will be
-     * informed of the adjunct before the predicate is tested.
+     * If a constraint is a {@link BrooklynObjectPredicate} then
+     * {@link BrooklynObjectPredicate#apply(Object, BrooklynObject)} will be used.
      */
     public static void assertValid(EntityAdjunct adjunct) {
         Iterable<ConfigKey<?>> violations = new EntityAdjunctConstraints(adjunct).getViolations();
@@ -114,7 +115,13 @@ public abstract class ConfigConstraints<T extends BrooklynObject> {
                 // keep its type to Predicte<? super T>, where T is ConfigKey<T>.
                 try {
                     Predicate<Object> po = (Predicate<Object>) configKey.getConstraint();
-                    if (!po.apply(value)) {
+                    boolean isValid;
+                    if (po instanceof BrooklynObjectPredicate) {
+                        isValid = BrooklynObjectPredicate.class.cast(po).apply(value, brooklynObject);
+                    } else {
+                        isValid = po.apply(value);
+                    }
+                    if (!isValid) {
                         violating.add(configKey);
                     }
                 } catch (Exception e) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/60d85fa4/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/NonDeploymentManagementContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/NonDeploymentManagementContext.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/NonDeploymentManagementContext.java
index 1f62add..5223cf8 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/NonDeploymentManagementContext.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/NonDeploymentManagementContext.java
@@ -102,7 +102,6 @@ public class NonDeploymentManagementContext implements ManagementContextInternal
     private NonDeploymentLocationManager locationManager;
     private NonDeploymentAccessManager accessManager;
     private NonDeploymentUsageManager usageManager;
-    private EntitlementManager entitlementManager;;
 
     public NonDeploymentManagementContext(AbstractEntity entity, NonDeploymentManagementContextMode mode) {
         this.entity = checkNotNull(entity, "entity");
@@ -113,10 +112,6 @@ public class NonDeploymentManagementContext implements ManagementContextInternal
         locationManager = new NonDeploymentLocationManager(null);
         accessManager = new NonDeploymentAccessManager(null);
         usageManager = new NonDeploymentUsageManager(null);
-        
-        // TODO might need to be some kind of "system" which can see that the system is running at this point
-        // though quite possibly we are entirely behind the auth-wall at this point
-        entitlementManager = Entitlements.minimal();
     }
 
     @Override
@@ -213,7 +208,15 @@ public class NonDeploymentManagementContext implements ManagementContextInternal
     
     @Override
     public Maybe<OsgiManager> getOsgiManager() {
-        return Maybe.absent();
+        switch (mode) {
+        case PRE_MANAGEMENT:
+        case MANAGEMENT_STARTING:
+        case MANAGEMENT_STARTED:
+            checkInitialManagementContextReal();
+            return initialManagementContext.getOsgiManager();
+        default:
+            return Maybe.absent("Entity " + entity + " is no longer managed; OSGi context no longer available");
+        }
     }
 
     @Override
@@ -335,7 +338,8 @@ public class NonDeploymentManagementContext implements ManagementContextInternal
     
     @Override
     public EntitlementManager getEntitlementManager() {
-        return entitlementManager;
+        checkInitialManagementContextReal();
+        return initialManagementContext.getEntitlementManager();
     }
     
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/60d85fa4/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectPredicate.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectPredicate.java b/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectPredicate.java
new file mode 100644
index 0000000..422e9f8
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectPredicate.java
@@ -0,0 +1,33 @@
+/*
+ * 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.core.objs;
+
+import org.apache.brooklyn.api.objs.BrooklynObject;
+import org.apache.brooklyn.util.guava.PredicateWithContext;
+
+/**
+ * A marker interface for predicates that can use a {@link BrooklynObject} in their {@link #apply} method.
+ */
+public interface BrooklynObjectPredicate<P> extends PredicateWithContext<P, BrooklynObject> {
+
+    @Override
+    boolean apply(P input, BrooklynObject context);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/60d85fa4/core/src/main/java/org/apache/brooklyn/util/core/ResourcePredicates.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/ResourcePredicates.java b/core/src/main/java/org/apache/brooklyn/util/core/ResourcePredicates.java
new file mode 100644
index 0000000..8bf0309
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/util/core/ResourcePredicates.java
@@ -0,0 +1,61 @@
+/*
+ * 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.core;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.api.objs.BrooklynObject;
+import org.apache.brooklyn.core.objs.BrooklynObjectPredicate;
+import org.apache.brooklyn.util.text.Strings;
+
+import com.google.common.base.Predicate;
+
+public class ResourcePredicates {
+
+    private ResourcePredicates() {}
+
+    /**
+     * @return A predicate that tests whether its input is a resource readable by Brooklyn.
+     * @see ResourceUtils#doesUrlExist(String)
+     */
+    public static Predicate<String> urlExists() {
+        return new ResourceExistsPredicate();
+    }
+
+    private static class ResourceExistsPredicate implements BrooklynObjectPredicate<String> {
+
+        @Override
+        public boolean apply(@Nullable String resource) {
+            return apply(resource, null);
+        }
+
+        @Override
+        public boolean apply(@Nullable String resource, @Nullable BrooklynObject context) {
+            return !Strings.isBlank(resource) && new ResourceUtils(context).doesUrlExist(resource);
+        }
+
+        @Override
+        public String toString() {
+            return "ResourcePredicates.exists()";
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/60d85fa4/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java b/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
index 6cf42ab..05e0814 100644
--- a/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
@@ -25,12 +25,14 @@ import static org.testng.Assert.fail;
 
 import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.api.objs.BrooklynObject;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
 import org.apache.brooklyn.api.sensor.EnricherSpec;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.enricher.AbstractEnricher;
 import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.objs.BrooklynObjectPredicate;
 import org.apache.brooklyn.core.policy.AbstractPolicy;
 import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
 import org.apache.brooklyn.core.test.entity.TestEntity;
@@ -48,6 +50,8 @@ import com.google.common.collect.Range;
 
 public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
 
+    // ----------- Setup -----------------------------------------------------------------------------------------------
+
     @ImplementedBy(EntityWithNonNullConstraintImpl.class)
     public static interface EntityWithNonNullConstraint extends TestEntity {
         ConfigKey<Object> NON_NULL_CONFIG = ConfigKeys.builder(Object.class)
@@ -56,6 +60,8 @@ public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
                 .constraint(Predicates.notNull())
                 .build();
     }
+    public static class EntityWithNonNullConstraintImpl extends TestEntityImpl implements EntityWithNonNullConstraint {
+    }
 
     @ImplementedBy(EntityWithNonNullConstraintWithNonNullDefaultImpl.class)
     public static interface EntityWithNonNullConstraintWithNonNullDefault extends TestEntity {
@@ -66,6 +72,8 @@ public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
                 .constraint(Predicates.notNull())
                 .build();
     }
+    public static class EntityWithNonNullConstraintWithNonNullDefaultImpl extends TestEntityImpl implements EntityWithNonNullConstraintWithNonNullDefault {
+    }
 
     @ImplementedBy(EntityRequiringConfigKeyInRangeImpl.class)
     public static interface EntityRequiringConfigKeyInRange extends TestEntity {
@@ -76,22 +84,25 @@ public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
                 .constraint(Range.closed(0, 9))
                 .build();
     }
+    public static class EntityRequiringConfigKeyInRangeImpl extends TestEntityImpl implements EntityRequiringConfigKeyInRange {
+    }
 
     @ImplementedBy(EntityProvidingDefaultValueForConfigKeyInRangeImpl.class)
     public static interface EntityProvidingDefaultValueForConfigKeyInRange extends EntityRequiringConfigKeyInRange {
         ConfigKey<Integer> REVISED_RANGE = ConfigKeys.newConfigKeyWithDefault(RANGE, -1);
     }
-
-    public static class EntityWithNonNullConstraintImpl extends TestEntityImpl implements EntityWithNonNullConstraint {
-    }
-
-    public static class EntityWithNonNullConstraintWithNonNullDefaultImpl extends TestEntityImpl implements EntityWithNonNullConstraintWithNonNullDefault {
+    public static class EntityProvidingDefaultValueForConfigKeyInRangeImpl extends TestEntityImpl implements EntityProvidingDefaultValueForConfigKeyInRange {
     }
 
-    public static class EntityRequiringConfigKeyInRangeImpl extends TestEntityImpl implements EntityRequiringConfigKeyInRange {
+    @ImplementedBy(EntityWithContextAwareConstraintImpl.class)
+    public static interface EntityWithContextAwareConstraint extends TestEntity {
+        ConfigKey<String> MUST_BE_DISPLAY_NAME = ConfigKeys.builder(String.class)
+                .name("must-be-display-name")
+                .description("Configuration key that must not be null")
+                .constraint(new MatchesEntityDisplayNamePredicate())
+                .build();
     }
-
-    public static class EntityProvidingDefaultValueForConfigKeyInRangeImpl extends TestEntityImpl implements EntityProvidingDefaultValueForConfigKeyInRange {
+    public static class EntityWithContextAwareConstraintImpl extends TestEntityImpl implements EntityWithContextAwareConstraint {
     }
 
     public static class PolicyWithConfigConstraint extends AbstractPolicy {
@@ -110,6 +121,20 @@ public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
                 .build();
     }
 
+    private static class MatchesEntityDisplayNamePredicate implements BrooklynObjectPredicate<String> {
+        @Override
+        public boolean apply(String input) {
+            return false;
+        }
+
+        @Override
+        public boolean apply(String input, BrooklynObject context) {
+            return context != null && context.getDisplayName().equals(input);
+        }
+    }
+
+    // ----------- Tests -----------------------------------------------------------------------------------------------
+
     @Test
     public void testExceptionWhenEntityHasNullConfig() {
         try {
@@ -240,7 +265,21 @@ public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
                     }
                 })
                 .build();
+        // i.e. no exception.
         assertFalse(key.isValueValid("abc"));
     }
 
+    @Test
+    public void testContextAwarePredicateInformedOfEntity() {
+        try {
+            app.createAndManageChild(EntitySpec.create(EntityWithContextAwareConstraint.class)
+                    .displayName("Mr. Big")
+                    .configure("must-be-display-name", "Mr. Bag"));
+            fail("Expected exception when managing entity with incorrect config");
+        } catch (Exception e) {
+            Throwable t = Exceptions.getFirstThrowableOfType(e, ConstraintViolationException.class);
+            assertNotNull(t);
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/60d85fa4/utils/common/src/main/java/org/apache/brooklyn/util/guava/PredicateWithContext.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/guava/PredicateWithContext.java b/utils/common/src/main/java/org/apache/brooklyn/util/guava/PredicateWithContext.java
new file mode 100644
index 0000000..9e8eb5e
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/guava/PredicateWithContext.java
@@ -0,0 +1,33 @@
+/*
+ * 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.guava;
+
+import com.google.common.base.Predicate;
+
+/**
+ * A marker interface for predicates that may take a context object into account.
+ * <p>
+ * Such predicates should handle absent contexts sensibly.
+ */
+public interface PredicateWithContext<P, T> extends Predicate<P> {
+
+    boolean apply(P input, T context);
+
+}


[14/17] incubator-brooklyn git commit: This closes #954

Posted by sj...@apache.org.
This closes #954


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/7efd062f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/7efd062f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/7efd062f

Branch: refs/heads/master
Commit: 7efd062f8c9b3a35cace49efd8f062b65389f0e2
Parents: 206d782 9751e02
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Wed Oct 14 14:50:25 2015 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Wed Oct 14 14:50:25 2015 +0100

----------------------------------------------------------------------
 docs/guide/yaml/creating-yaml.md | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
----------------------------------------------------------------------



[12/17] incubator-brooklyn git commit: Fixes @ApiParam values in EntityApi

Posted by sj...@apache.org.
Fixes @ApiParam values in EntityApi


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

Branch: refs/heads/master
Commit: e278257189f8488ca8ec0cbd0bef0b0195256f04
Parents: 49eabd1
Author: Martin Harris <gi...@nakomis.com>
Authored: Wed Oct 14 13:10:35 2015 +0100
Committer: Martin Harris <gi...@nakomis.com>
Committed: Wed Oct 14 13:10:35 2015 +0100

----------------------------------------------------------------------
 .../main/java/org/apache/brooklyn/rest/api/EntityApi.java    | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2782571/usage/rest-api/src/main/java/org/apache/brooklyn/rest/api/EntityApi.java
----------------------------------------------------------------------
diff --git a/usage/rest-api/src/main/java/org/apache/brooklyn/rest/api/EntityApi.java b/usage/rest-api/src/main/java/org/apache/brooklyn/rest/api/EntityApi.java
index 3be4f17..4546471 100644
--- a/usage/rest-api/src/main/java/org/apache/brooklyn/rest/api/EntityApi.java
+++ b/usage/rest-api/src/main/java/org/apache/brooklyn/rest/api/EntityApi.java
@@ -117,8 +117,8 @@ public interface EntityApi {
             @ApiError(code = 404, reason = "Could not find application or entity")
     })
     public List<TaskSummary> listTasks(
-            @ApiParam(value = "Entity ID or name", required = true) @PathParam("application") String applicationId,
-            @ApiParam(value = "Application ID or name", required = true) @PathParam("entity") String entityId);
+            @ApiParam(value = "Application ID or name", required = true) @PathParam("application") String applicationId,
+            @ApiParam(value = "Entity ID or name", required = true) @PathParam("entity") String entityId);
 
     @GET
     @Path("/{entity}/activities/{task}")
@@ -146,8 +146,8 @@ public interface EntityApi {
             @ApiError(code = 404, reason = "Could not find application or entity")
     })
     public List<Object> listTags(
-            @ApiParam(value = "Entity ID or name", required = true) @PathParam("application") String applicationId,
-            @ApiParam(value = "Application ID or name", required = true) @PathParam("entity") String entityId);
+            @ApiParam(value = "Application ID or name", required = true) @PathParam("application") String applicationId,
+            @ApiParam(value = "Entity ID or name", required = true) @PathParam("entity") String entityId);
 
     @POST
     @ApiOperation(


[04/17] incubator-brooklyn git commit: Config constraint improvements

Posted by sj...@apache.org.
Config constraint improvements

* Entity descendants are checked at management
* Adds ConfigKey#isValueValid(T)
* Incorporates into ApplicationResource#create


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/7a728b48
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/7a728b48
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/7a728b48

Branch: refs/heads/master
Commit: 7a728b48ce327692320e345193393bbe6edcba2a
Parents: 8ff866b
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Fri Sep 11 13:47:26 2015 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Thu Oct 8 17:51:27 2015 +0100

----------------------------------------------------------------------
 .../brooklyn/core/config/BasicConfigKey.java    | 24 +++++++-
 .../brooklyn/core/config/ConfigConstraints.java | 39 +++++++++---
 .../config/ConstraintViolationException.java    | 38 ++++++++++++
 .../mgmt/internal/EntityManagementSupport.java  |  5 +-
 .../core/mgmt/internal/LocalEntityManager.java  | 14 ++---
 .../core/objs/proxy/InternalEntityFactory.java  | 37 +++++++++---
 .../core/config/ConfigKeyConstraintTest.java    | 63 ++++++++++++++++++--
 .../rest/resources/ApplicationResource.java     |  8 ++-
 .../org/apache/brooklyn/config/ConfigKey.java   |  9 ++-
 9 files changed, 196 insertions(+), 41 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7a728b48/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java b/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
index 9056eac..fe5e064 100644
--- a/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
+++ b/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
@@ -88,7 +88,7 @@ public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializab
         private String description;
         private T defaultValue;
         private boolean reconfigurable;
-        private Predicate<? super T> constraint;
+        private Predicate<? super T> constraint = Predicates.alwaysTrue();
         private ConfigInheritance inheritance;
         
         public Builder<T> name(String val) {
@@ -173,9 +173,12 @@ public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializab
         this.defaultValue = builder.defaultValue;
         this.reconfigurable = builder.reconfigurable;
         this.inheritance = builder.inheritance;
-        this.constraint = builder.constraint;
+        // Note: it's intentionally possible to have default values that are not valid
+        // per the configured constraint. If validity were checked here any class that
+        // contained a weirdly-defined config key would fail to initialise.
+        this.constraint = checkNotNull(builder.constraint, "constraint");
     }
-    
+
     /** @see ConfigKey#getName() */
     @Override public String getName() { return name; }
 
@@ -199,21 +202,36 @@ public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializab
         return defaultValue != null;
     }
 
+    /** @see ConfigKey#isReconfigurable() */
     @Override
     public boolean isReconfigurable() {
         return reconfigurable;
     }
     
+    /** @see ConfigKey#getInheritance() */
     @Override @Nullable
     public ConfigInheritance getInheritance() {
         return inheritance;
     }
 
+    /** @see ConfigKey#getConstraint() */
     @Override @Nonnull
     public Predicate<? super T> getConstraint() {
         return constraint;
     }
 
+    /** @see ConfigKey#isValueValid(T) */
+    @Override
+    public boolean isValueValid(T value) {
+        // The likeliest source of an exception is a constraint from Guava that expects a non-null input.
+        try {
+            return getConstraint().apply(value);
+        } catch (Exception e) {
+            log.debug("Suppressing exception when testing validity of " + this, e);
+            return false;
+        }
+    }
+
     /** @see ConfigKey#getNameParts() */
     @Override public Collection<String> getNameParts() {
         return Lists.newArrayList(dots.split(name));

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7a728b48/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java b/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
index ce6f39b..24142a5 100644
--- a/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
+++ b/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
@@ -19,6 +19,7 @@
 
 package org.apache.brooklyn.core.config;
 
+import java.util.Iterator;
 import java.util.List;
 
 import org.apache.brooklyn.api.entity.Entity;
@@ -42,21 +43,47 @@ public abstract class ConfigConstraints<T extends BrooklynObject> {
 
     /**
      * Checks all constraints of all config keys available to an entity.
+     * <p>
+     * If a constraint is a {@link BrooklynObjectAwarePredicate} then it will be
+     * informed of the entity before the predicate is tested.
      */
     public static void assertValid(Entity entity) {
         Iterable<ConfigKey<?>> violations = new EntityConfigConstraints(entity).getViolations();
         if (!Iterables.isEmpty(violations)) {
-            throw new ConstraintViolationException("ConfigKeys violate constraints: " + violations);
+            throw new ConstraintViolationException(errorMessage(entity, violations));
         }
     }
 
+    /**
+     * Checks all constraints of all config keys available to an entity adjunct.
+     * <p>
+     * If a constraint is a {@link BrooklynObjectAwarePredicate} then it will be
+     * informed of the adjunct before the predicate is tested.
+     */
     public static void assertValid(EntityAdjunct adjunct) {
         Iterable<ConfigKey<?>> violations = new EntityAdjunctConstraints(adjunct).getViolations();
         if (!Iterables.isEmpty(violations)) {
-            throw new ConstraintViolationException("ConfigKeys violate constraints: " + violations);
+            throw new ConstraintViolationException(errorMessage(adjunct, violations));
         }
     }
 
+    private static String errorMessage(BrooklynObject object, Iterable<ConfigKey<?>> violations) {
+        StringBuilder message = new StringBuilder("Error configuring ")
+                .append(object.getDisplayName())
+                .append(": [");
+        Iterator<ConfigKey<?>> it = violations.iterator();
+        while (it.hasNext()) {
+            ConfigKey<?> config = it.next();
+            message.append(config.getName())
+                    .append(":")
+                    .append(config.getConstraint());
+            if (it.hasNext()) {
+                message.append(", ");
+            }
+        }
+        return message.append("]").toString();
+    }
+
     public ConfigConstraints(T brooklynObject) {
         this.brooklynObject = brooklynObject;
     }
@@ -73,7 +100,7 @@ public abstract class ConfigConstraints<T extends BrooklynObject> {
 
     @SuppressWarnings("unchecked")
     private Iterable<ConfigKey<?>> validateAll() {
-        List<ConfigKey<?>> violating = Lists.newArrayList();
+        List<ConfigKey<?>> violating = Lists.newLinkedList();
         BrooklynObjectInternal.ConfigurationSupportInternal configInternal = getConfigurationSupportInternal();
 
         Iterable<ConfigKey<?>> configKeys = getBrooklynObjectTypeConfigKeys();
@@ -128,10 +155,4 @@ public abstract class ConfigConstraints<T extends BrooklynObject> {
         }
     }
 
-    public static class ConstraintViolationException extends RuntimeException {
-        public ConstraintViolationException(String message) {
-            super(message);
-        }
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7a728b48/core/src/main/java/org/apache/brooklyn/core/config/ConstraintViolationException.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/config/ConstraintViolationException.java b/core/src/main/java/org/apache/brooklyn/core/config/ConstraintViolationException.java
new file mode 100644
index 0000000..55c7f07
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/config/ConstraintViolationException.java
@@ -0,0 +1,38 @@
+/*
+ * 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.core.config;
+
+/**
+ * A {@link ConstraintViolationException} indicates one or more problems applying
+ * values for {@link org.apache.brooklyn.config.ConfigKey ConfigKeys} when creating
+ * a {@link org.apache.brooklyn.api.objs.BrooklynObject}.
+ */
+public class ConstraintViolationException extends RuntimeException {
+    private static final long serialVersionUID = -6719912119648996815L;
+
+    public ConstraintViolationException(String message) {
+        super(message);
+    }
+
+    public ConstraintViolationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7a728b48/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/EntityManagementSupport.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/EntityManagementSupport.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/EntityManagementSupport.java
index c7d3d04..6b61bf6 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/EntityManagementSupport.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/EntityManagementSupport.java
@@ -65,7 +65,7 @@ import com.google.common.base.Stopwatch;
  * <p>
  * on unmanage it hits onManagementStoppingHere() then onManagementStopping().
  * <p>
- * When an entity's management migrates, it invoked onManagementStoppingHere() at the old location,
+ * When an entity's management migrates, it invokes onManagementStoppingHere() at the old location,
  * then onManagementStartingHere() at the new location.
  */
 public class EntityManagementSupport {
@@ -85,9 +85,6 @@ public class EntityManagementSupport {
     protected transient SubscriptionContext subscriptionContext;
     protected transient ExecutionContext executionContext;
     
-    // TODO the application
-    // (elaborate or remove ^^^ ? -AH, Sept 2014)
-    
     protected final AtomicBoolean managementContextUsable = new AtomicBoolean(false);
     protected final AtomicBoolean currentlyDeployed = new AtomicBoolean(false);
     protected final AtomicBoolean everDeployed = new AtomicBoolean(false);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7a728b48/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalEntityManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalEntityManager.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalEntityManager.java
index f1fde20..6c9022d 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalEntityManager.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalEntityManager.java
@@ -44,7 +44,6 @@ import org.apache.brooklyn.api.policy.PolicySpec;
 import org.apache.brooklyn.api.sensor.Enricher;
 import org.apache.brooklyn.api.sensor.EnricherSpec;
 import org.apache.brooklyn.core.BrooklynLogging;
-import org.apache.brooklyn.core.config.ConfigConstraints;
 import org.apache.brooklyn.core.entity.AbstractEntity;
 import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.entity.EntityInternal;
@@ -269,8 +268,6 @@ public class LocalEntityManager implements EntityManagerInternal {
                     new Exception("source of duplicate management of "+e));
             return;
         }
-        ConfigConstraints.assertValid(e);
-
         manageRecursive(e, ManagementTransitionMode.guessing(BrooklynObjectManagementMode.NONEXISTENT, BrooklynObjectManagementMode.MANAGED_PRIMARY));
     }
 
@@ -311,7 +308,7 @@ public class LocalEntityManager implements EntityManagerInternal {
     protected void manageRecursive(Entity e, final ManagementTransitionMode initialMode) {
         checkManagementAllowed(e);
 
-        final List<EntityInternal> allEntities =  Lists.newArrayList();
+        final List<EntityInternal> allEntities = Lists.newArrayList();
         Predicate<EntityInternal> manageEntity = new Predicate<EntityInternal>() { public boolean apply(EntityInternal it) {
             ManagementTransitionMode mode = getLastManagementTransitionMode(it.getId());
             if (mode==null) {
@@ -390,7 +387,7 @@ public class LocalEntityManager implements EntityManagerInternal {
             }
         }
     }
-    
+
     @Override
     public void unmanage(final Entity e) {
         unmanage(e, ManagementTransitionMode.guessing(BrooklynObjectManagementMode.MANAGED_PRIMARY, BrooklynObjectManagementMode.NONEXISTENT));
@@ -600,7 +597,7 @@ public class LocalEntityManager implements EntityManagerInternal {
     /**
      * Should ensure that the entity is now known about, but should not be accessible from other entities yet.
      * 
-     * Records that the given entity is about to be managed (used for answering {@link isPreManaged(Entity)}.
+     * Records that the given entity is about to be managed (used for answering {@link #isPreManaged(Entity)}.
      * Note that refs to the given entity are stored in a a weak hashmap so if the subsequent management
      * attempt fails then this reference to the entity will eventually be discarded (if no-one else holds 
      * a reference).
@@ -628,7 +625,6 @@ public class LocalEntityManager implements EntityManagerInternal {
     /**
      * Should ensure that the entity is now managed somewhere, and known about in all the lists.
      * Returns true if the entity has now become managed; false if it was already managed (anything else throws exception)
-     * @param isOrWasReadOnly 
      */
     private synchronized boolean manageNonRecursive(Entity e, ManagementTransitionMode mode) {
         Entity old = entitiesById.get(e.getId());
@@ -641,8 +637,8 @@ public class LocalEntityManager implements EntityManagerInternal {
             }
             return false;
         }
-        
-        BrooklynLogging.log(log, BrooklynLogging.levelDebugOrTraceIfReadOnly(e), 
+
+        BrooklynLogging.log(log, BrooklynLogging.levelDebugOrTraceIfReadOnly(e),
             "{} starting management of entity {}", this, e);
         Entity realE = toRealEntity(e);
         

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7a728b48/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java b/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
index f258cdc..9552583 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
@@ -24,6 +24,7 @@ import static com.google.common.base.Preconditions.checkState;
 import java.lang.reflect.InvocationTargetException;
 import java.util.Collection;
 import java.util.Map;
+import java.util.Queue;
 import java.util.Set;
 
 import org.apache.brooklyn.api.entity.Entity;
@@ -37,6 +38,7 @@ import org.apache.brooklyn.api.policy.PolicySpec;
 import org.apache.brooklyn.api.sensor.Enricher;
 import org.apache.brooklyn.api.sensor.EnricherSpec;
 import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigConstraints;
 import org.apache.brooklyn.core.entity.AbstractApplication;
 import org.apache.brooklyn.core.entity.AbstractEntity;
 import org.apache.brooklyn.core.entity.Entities;
@@ -56,6 +58,7 @@ import org.slf4j.LoggerFactory;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 
 /**
@@ -258,6 +261,20 @@ public class InternalEntityFactory extends InternalFactory {
             throw Exceptions.propagate(e);
         }
     }
+
+    /**
+     * Calls {@link ConfigConstraints#assertValid(Entity)} on the given entity and all of
+     * its descendants.
+     */
+    private void validateDescendantConfig(Entity e) {
+        Queue<Entity> queue = Lists.newLinkedList();
+        queue.add(e);
+        while (!queue.isEmpty()) {
+            Entity e1 = queue.poll();
+            ConfigConstraints.assertValid(e1);
+            queue.addAll(e1.getChildren());
+        }
+    }
     
     protected <T extends Entity> void initEntityAndDescendants(String entityId, final Map<String,Entity> entitiesByEntityId, final Map<String,EntitySpec<?>> specsByEntityId) {
         final Entity entity = entitiesByEntityId.get(entityId);
@@ -269,7 +286,11 @@ public class InternalEntityFactory extends InternalFactory {
                 + "and thus it should be already fully initialized.");
             return;
         }
-        
+
+        // Validate all config before attempting to manage any entity. Do this here rather
+        // than in manageRecursive so that rebind is unaffected.
+        validateDescendantConfig(entity);
+
         /* Marked transient so that the task is not needlessly kept around at the highest level.
          * Note that the task is not normally visible in the GUI, because 
          * (a) while it is running, the entity is parentless (and so not in the tree);
@@ -286,36 +307,36 @@ public class InternalEntityFactory extends InternalFactory {
             @Override
             public void run() {
                 ((AbstractEntity)entity).init();
-                
+
                 ((AbstractEntity)entity).addLocations(spec.getLocations());
 
                 for (EntityInitializer initializer: spec.getInitializers()) {
                     initializer.apply((EntityInternal)entity);
                 }
-                
+
                 for (Enricher enricher : spec.getEnrichers()) {
                     entity.enrichers().add(enricher);
                 }
-                
+
                 for (EnricherSpec<?> enricherSpec : spec.getEnricherSpecs()) {
                     entity.enrichers().add(policyFactory.createEnricher(enricherSpec));
                 }
-                
+
                 for (Policy policy : spec.getPolicies()) {
                     entity.policies().add((AbstractPolicy)policy);
                 }
-                
+
                 for (PolicySpec<?> policySpec : spec.getPolicySpecs()) {
                     entity.policies().add(policyFactory.createPolicy(policySpec));
                 }
-                                
+
                 for (Entity child: entity.getChildren()) {
                     // right now descendants are initialized depth-first (see the getUnchecked() call below)
                     // they could be done in parallel, but OTOH initializers should be very quick
                     initEntityAndDescendants(child.getId(), entitiesByEntityId, specsByEntityId);
                 }
             }
-        }).build()).getUnchecked();        
+        }).build()).getUnchecked();
     }
     
     /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7a728b48/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java b/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
index 6636944..6cf42ab 100644
--- a/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyConstraintTest.java
@@ -19,6 +19,7 @@
 
 package org.apache.brooklyn.core.config;
 
+import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.fail;
 
@@ -29,6 +30,7 @@ import org.apache.brooklyn.api.policy.PolicySpec;
 import org.apache.brooklyn.api.sensor.EnricherSpec;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.enricher.AbstractEnricher;
+import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.policy.AbstractPolicy;
 import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
 import org.apache.brooklyn.core.test.entity.TestEntity;
@@ -37,9 +39,11 @@ import org.apache.brooklyn.core.test.policy.TestPolicy;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.net.Networking;
 import org.testng.annotations.Test;
-import org.apache.brooklyn.core.config.ConfigConstraints.ConstraintViolationException;
 
+import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Range;
 
 public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
@@ -90,7 +94,7 @@ public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
     public static class EntityProvidingDefaultValueForConfigKeyInRangeImpl extends TestEntityImpl implements EntityProvidingDefaultValueForConfigKeyInRange {
     }
 
-    public static class PolicyWithConfigConstraint extends AbstractPolicy{
+    public static class PolicyWithConfigConstraint extends AbstractPolicy {
         public static final ConfigKey<Object> NON_NULL_CONFIG = ConfigKeys.builder(Object.class)
                 .name("test.policy.non-null")
                 .description("Configuration key that must not be null")
@@ -106,7 +110,6 @@ public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
                 .build();
     }
 
-
     @Test
     public void testExceptionWhenEntityHasNullConfig() {
         try {
@@ -141,7 +144,7 @@ public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
     }
 
     @Test
-    public void testExceptionIsThrownWhenUserSetsNullValueToConfigWithNonNullDefault() {
+    public void testExceptionIsThrownWhenUserNullsConfigWithNonNullDefault() {
         try {
             app.createAndManageChild(EntitySpec.create(EntityWithNonNullConstraintWithNonNullDefault.class)
                     .configure(EntityWithNonNullConstraintWithNonNullDefault.NON_NULL_WITH_DEFAULT, (Object) null));
@@ -152,6 +155,33 @@ public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
         }
     }
 
+    @Test
+    public void testExceptionWhenValueSetByName() {
+        try {
+            app.createAndManageChild(EntitySpec.create(EntityRequiringConfigKeyInRange.class)
+                    .configure(ImmutableMap.of("test.conf.range", -1)));
+            fail("Expected exception when managing entity with invalid config");
+        } catch (Exception e) {
+            Throwable t = Exceptions.getFirstThrowableOfType(e, ConstraintViolationException.class);
+            assertNotNull(t);
+        }
+    }
+
+    @Test
+    public void testExceptionWhenAppGrandchildHasInvalidConfig() {
+        app.start(ImmutableList.of(app.newSimulatedLocation()));
+        TestEntity testEntity = app.addChild(EntitySpec.create(TestEntity.class));
+        testEntity.addChild(EntitySpec.create(EntityRequiringConfigKeyInRange.class)
+                .configure(EntityRequiringConfigKeyInRange.RANGE, -1));
+        try {
+            Entities.manage(testEntity);
+            fail("Expected exception when managing child with invalid config");
+        } catch (Exception e) {
+            Throwable t = Exceptions.getFirstThrowableOfType(e, ConstraintViolationException.class);
+            assertNotNull(t);
+        }
+    }
+
     // Test fails because config keys that are not on an object's interfaces cannot be checked automatically.
     @Test(enabled = false)
     public void testExceptionWhenPolicyHasNullForeignConfig() {
@@ -167,7 +197,7 @@ public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
     }
 
     @Test
-    public void testExceptionWhenPolicyHasNullConfig() {
+    public void testExceptionWhenPolicyHasInvalidConfig() {
         try {
             mgmt.getEntityManager().createPolicy(PolicySpec.create(PolicyWithConfigConstraint.class)
                     .configure(PolicyWithConfigConstraint.NON_NULL_CONFIG, (Object) null));
@@ -190,4 +220,27 @@ public class ConfigKeyConstraintTest extends BrooklynAppUnitTestSupport {
         }
     }
 
+    @Test
+    public void testDefaultValueDoesNotNeedToObeyConstraint() {
+        ConfigKeys.builder(String.class)
+                .name("foo")
+                .defaultValue("a")
+                .constraint(Predicates.equalTo("b"))
+                .build();
+    }
+
+    @Test
+    public void testIsValidWithBadlyBehavedPredicate() {
+        ConfigKey<String> key = ConfigKeys.builder(String.class)
+                .name("foo")
+                .constraint(new Predicate<String>() {
+                    @Override
+                    public boolean apply(String input) {
+                        throw new RuntimeException("It's my day off");
+                    }
+                })
+                .build();
+        assertFalse(key.isValueValid("abc"));
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7a728b48/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
index 33752b2..68a9198 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
@@ -45,6 +45,7 @@ import org.apache.brooklyn.api.mgmt.Task;
 import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.api.sensor.Sensor;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
+import org.apache.brooklyn.core.config.ConstraintViolationException;
 import org.apache.brooklyn.core.entity.Attributes;
 import org.apache.brooklyn.core.entity.EntityPredicates;
 import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
@@ -71,6 +72,7 @@ import org.apache.brooklyn.rest.util.WebResourceUtils;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.core.ResourceUtils;
 import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.exceptions.UserFacingException;
 import org.apache.brooklyn.util.text.Strings;
 import org.codehaus.jackson.JsonNode;
 import org.codehaus.jackson.node.ArrayNode;
@@ -286,7 +288,7 @@ public class ApplicationResource extends AbstractBrooklynRestResource implements
         try {
             Application app = EntityManagementUtils.createUnstarted(mgmt(), spec);
             CreationResult<Application,Void> result = EntityManagementUtils.start(app);
-            
+
             boolean isEntitled = Entitlements.isEntitled(
                     mgmt().getEntitlementManager(),
                     Entitlements.INVOKE_EFFECTOR,
@@ -301,9 +303,11 @@ public class ApplicationResource extends AbstractBrooklynRestResource implements
 
             URI ref = URI.create(app.getApplicationId());
             ResponseBuilder response = created(ref);
-            if (result.task() != null) 
+            if (result.task() != null)
                 response.entity(TaskTransformer.FROM_TASK.apply(result.task()));
             return response.build();
+        } catch (ConstraintViolationException e) {
+            throw new UserFacingException(e);
         } catch (Exception e) {
             throw Exceptions.propagate(e);
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7a728b48/utils/common/src/main/java/org/apache/brooklyn/config/ConfigKey.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/config/ConfigKey.java b/utils/common/src/main/java/org/apache/brooklyn/config/ConfigKey.java
index 9dab87d..92f174f 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/config/ConfigKey.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/config/ConfigKey.java
@@ -89,12 +89,19 @@ public interface ConfigKey<T> {
     @Nullable ConfigInheritance getInheritance();
 
     /**
-     * @return the predicate constraining the key's value.
+     * @return The predicate constraining the key's value.
      */
     @Beta
     @Nonnull
     Predicate<? super T> getConstraint();
 
+    /**
+     * @param value The value to test
+     * @return True if the given value is acceptable per the {@link #getConstraint constraints} on this key.
+     */
+    @Beta
+    boolean isValueValid(T value);
+
     /** Interface for elements which want to be treated as a config key without actually being one
      * (e.g. config attribute sensors).
      */