brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From henev...@apache.org
Subject [5/6] git commit: use generics for RendererHint, simplify class hierarchy there, and add support for rendering based on class type, used for Duration and HostAndPort, and for Entity instances to show the name of the entity
Date Tue, 07 Oct 2014 04:18:52 GMT
use generics for RendererHint, simplify class hierarchy there, and add support for rendering based on class type, used for Duration and HostAndPort, and for Entity instances to show the name of the entity


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

Branch: refs/heads/master
Commit: b567f1a5cfb276dc63a9131f16a848ddb7b1312b
Parents: 552f3cf
Author: Alex Heneveld <alex.heneveld@cloudsoftcorp.com>
Authored: Fri Oct 3 05:23:13 2014 +0100
Committer: Alex Heneveld <alex.heneveld@cloudsoftcorp.com>
Committed: Fri Oct 3 11:59:12 2014 +0100

----------------------------------------------------------------------
 .../java/brooklyn/entity/basic/EntityLocal.java |  15 --
 .../brooklyn/config/render/RendererHints.java   | 179 +++++++++++++------
 .../brooklyn/entity/basic/AbstractEntity.java   |   5 +
 .../brooklyn/entity/basic/DelegateEntity.java   |  28 ++-
 .../brooklyn/entity/basic/EntityPredicates.java |   2 +
 .../java/brooklyn/entity/basic/Lifecycle.java   |   7 +-
 .../entity/group/DynamicClusterImpl.java        |   4 +-
 .../entity/brooklynnode/BrooklynNodeImpl.java   |   6 +-
 .../entity/machine/MachineAttributes.java       |   3 -
 .../brooklyn/entity/messaging/storm/Storm.java  |   2 +-
 .../entity/nosql/couchbase/CouchbaseNode.java   |   2 +-
 .../couchbase/CouchbaseSyncGatewayImpl.java     |   2 +-
 .../entity/nosql/mongodb/MongoDBServerImpl.java |   2 +-
 .../entity/webapp/WebAppServiceConstants.java   |   2 +-
 .../entity/webapp/WebAppServiceMetrics.java     |   8 +-
 .../entity/webapp/jboss/JBoss7Server.java       |   1 +
 .../entity/webapp/jboss/JBoss7ServerImpl.java   |   6 +
 .../entity/webapp/jboss/JBoss7SshDriver.java    |   3 +-
 .../rest/resources/EntityConfigResource.java    |   6 +-
 .../brooklyn/rest/resources/SensorResource.java |  20 +--
 .../brooklyn/rest/resources/ServerResource.java |   5 +-
 .../rest/transform/SensorTransformer.java       |  16 +-
 .../ErrorAndToStringUnknownTypeSerializer.java  |   2 +-
 .../brooklyn/rest/domain/SensorSummaryTest.java |   4 +-
 .../src/main/java/brooklyn/util/time/Time.java  |  50 +++++-
 25 files changed, 235 insertions(+), 145 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/api/src/main/java/brooklyn/entity/basic/EntityLocal.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/entity/basic/EntityLocal.java b/api/src/main/java/brooklyn/entity/basic/EntityLocal.java
index b38a3d8..3405443 100644
--- a/api/src/main/java/brooklyn/entity/basic/EntityLocal.java
+++ b/api/src/main/java/brooklyn/entity/basic/EntityLocal.java
@@ -75,21 +75,6 @@ public interface EntityLocal extends Entity, Configurable {
      */
     <T> T setAttribute(AttributeSensor<T> sensor, T val);
 
-//    /** sets the value of the given attribute sensor from the config key value herein,
-//     * if the config key resolves to a non-null value as a sensor
-//     * 
-//     * @deprecated since 0.5; use {@link #setAttribute(AttributeSensor, Object)}, such as 
-//     * <pre>
-//     * T val = getConfig(KEY.getConfigKey());
-//     * if (val != null) {
-//     *     setAttribute(KEY, val)
-//     * }
-//     * </pre>
-//     * 
-//     * @return old value
-//     */
-//    <T> T setAttribute(AttributeSensorAndConfigKey<?,T> configuredSensor);
-
     /**
      * @deprecated in 0.5; use {@link #getConfig(ConfigKey)}
      */

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/core/src/main/java/brooklyn/config/render/RendererHints.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/config/render/RendererHints.java b/core/src/main/java/brooklyn/config/render/RendererHints.java
index 491e5f1..dcee04d 100644
--- a/core/src/main/java/brooklyn/config/render/RendererHints.java
+++ b/core/src/main/java/brooklyn/config/render/RendererHints.java
@@ -18,35 +18,47 @@
  */
 package brooklyn.config.render;
 
-import brooklyn.config.ConfigKey;
 import groovy.lang.Closure;
 
+import java.net.URL;
 import java.util.Set;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.ConfigKey;
 import brooklyn.entity.Entity;
 import brooklyn.event.AttributeSensor;
-import brooklyn.event.Sensor;
 import brooklyn.util.GroovyJavaMethods;
+import brooklyn.util.net.UserAndHostAndPort;
+import brooklyn.util.text.StringFunctions;
+import brooklyn.util.time.Duration;
+import brooklyn.util.time.Time;
 
 import com.google.common.annotations.Beta;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
 import com.google.common.base.Functions;
 import com.google.common.base.Objects;
+import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicates;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.LinkedHashMultimap;
 import com.google.common.collect.Multimaps;
 import com.google.common.collect.SetMultimap;
 import com.google.common.collect.Sets;
+import com.google.common.net.HostAndPort;
 
 /**
  * Registry of hints for displaying items such as sensors, e.g. in the web console.
  */
 public class RendererHints {
 
+    private static final Logger log = LoggerFactory.getLogger(RendererHints.class);
+    
     @VisibleForTesting
     static SetMultimap<Object, Hint<?>> registry = Multimaps.synchronizedSetMultimap(LinkedHashMultimap.<Object, Hint<?>>create());
 
@@ -55,24 +67,81 @@ public class RendererHints {
      * <p>
      * Returns the element, for convenience when used in a with block after defining the element.
      */
-    public static <T> T register(T element, Hint<T> hintForThatElement) {
+    public static <T> AttributeSensor<T> register(AttributeSensor<T> element, Hint<? super T> hintForThatElement) { return _register(element, hintForThatElement); }
+    /** as {@link #register(AttributeSensor, Hint)} */
+    public static <T> ConfigKey<T> register(ConfigKey<T> element, Hint<? super T> hintForThatElement) { return _register(element, hintForThatElement); }
+    /** as {@link #register(AttributeSensor, Hint)} */
+    public static <T> Class<T> register(Class<T> element, Hint<? super T> hintForThatElement) { return _register(element, hintForThatElement); }
+    
+    private static <T> T _register(T element, Hint<?> hintForThatElement) {
+        if (element==null) {
+            // can happen if being done in a static initializer in an inner class
+            log.error("Invalid null target for renderer hint "+hintForThatElement, new Throwable("Trace for invalid null target for renderer hint"));
+        }
         registry.put(element, hintForThatElement);
         return element;
     }
 
-    public static Set<Hint<?>> getHintsFor(Object element) {
-         return getHintsFor(element, Hint.class);
-    }
+    /** Returns all registered hints against the given element */
+    public static Set<Hint<?>> getHintsFor(AttributeSensor<?> element) { return _getHintsFor(element, null); }
+    /** as {@link #getHintsFor(AttributeSensor)} */
+    public static Set<Hint<?>> getHintsFor(ConfigKey<?> element) { return _getHintsFor(element, null); }
+    /** as {@link #getHintsFor(AttributeSensor)} */
+    public static Set<Hint<?>> getHintsFor(Class<?> element) { return _getHintsFor(element, null); }
 
-    public static Set<Hint<?>> getHintsFor(Object element, Class<? extends Hint> optionalHintSuperClass) {
+    @Deprecated /** @deprecated since 0.7.0 only supported for certain types */
+    public static Set<Hint<?>> getHintsFor(Object element) { return getHintsFor(element, null); }
+
+    @Deprecated /** @deprecated since 0.7.0 only supported for certain types */
+    @SuppressWarnings("rawtypes")
+    public static Set<Hint<?>> getHintsFor(Object element, Class<? extends Hint> optionalHintSuperClass) { return getHintsFor(element, optionalHintSuperClass); }
+    
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    private static <T extends Hint> Set<T> _getHintsFor(Object element, Class<T> optionalHintSuperClass) {
         Set<Hint<?>> found = ImmutableSet.copyOf(registry.get(element));
+        if (found.isEmpty() && element instanceof Class && !Object.class.equals(element)) {
+            // try superclasses of the element; this seems overkill for the main use case, Entity;
+            // (other classes registered are typically final)
+            found = (Set<Hint<?>>) _getHintsFor(((Class)element).getSuperclass(), optionalHintSuperClass);
+            if (found.isEmpty()) {
+                for (Class<?> parentInterface: ((Class)element).getInterfaces()) {
+                    found = (Set<Hint<?>>) _getHintsFor(parentInterface, optionalHintSuperClass);
+                    if (!found.isEmpty())
+                        break;
+                }
+            }
+        }
         if (optionalHintSuperClass != null) {
-            return Sets.filter(found, Predicates.instanceOf(optionalHintSuperClass));
+            return (Set<T>)Sets.filter(found, Predicates.instanceOf(optionalHintSuperClass));
         } else {
-            return found;
+            return (Set<T>)found;
+        }
+    }
+
+    /** Applies the (first) display value hint registered against the given target to the given initialValue */  
+    public static Object applyDisplayValueHint(AttributeSensor<?> target, Object initialValue) { return _applyDisplayValueHint(target, initialValue); }
+    /** as {@link #applyDisplayValueHint(AttributeSensor, Object)} */
+    public static Object applyDisplayValueHint(ConfigKey<?> target, Object initialValue) { return _applyDisplayValueHint(target, initialValue); }
+    /** as {@link #applyDisplayValueHint(AttributeSensor, Object)} */
+    public static Object applyDisplayValueHint(Class<?> target, Object initialValue) { return _applyDisplayValueHint(target, initialValue); }
+    
+    private static Object _applyDisplayValueHint(Object target, Object initialValue) { return _applyDisplayValueHint(target, initialValue, true); }
+    @SuppressWarnings("rawtypes")
+    private static Object _applyDisplayValueHint(Object target, Object initialValue, boolean includeClass) {
+        Iterable<RendererHints.DisplayValue> hints = RendererHints._getHintsFor(target, RendererHints.DisplayValue.class);
+        if (Iterables.size(hints) > 1) {
+            log.warn("Multiple display value hints set for {}; Only one will be applied, using first", target);
+        }
+
+        Optional<RendererHints.DisplayValue> hint = Optional.fromNullable(Iterables.getFirst(hints, null));
+        Object value = hint.isPresent() ? hint.get().getDisplayValue(initialValue) : initialValue;
+        if (includeClass && value!=null && !(value instanceof String) && !(value instanceof Number) && !(value.getClass().isPrimitive())) {
+            value = _applyDisplayValueHint(value.getClass(), value, false);
         }
+        return value;
     }
 
+
     /** Parent marker class for hints. */
     public static abstract class Hint<T> { }
 
@@ -84,25 +153,26 @@ public class RendererHints {
      * This hint describes a named action possible on something, e.g. a sensor;
      * currently used in web client to show actions on sensors
      */
-    public static class NamedActionWithUrl extends Hint<Sensor> implements NamedAction {
+    public static class NamedActionWithUrl<T> extends Hint<T> implements NamedAction {
         private final String actionName;
-        private final Function<Object, String> postProcessing;
+        private final Function<T, String> postProcessing;
 
         public NamedActionWithUrl(String actionName) {
-            this(actionName, (Function<Object, String>)null);
+            this(actionName, (Function<T, String>)null);
         }
 
+        @SuppressWarnings("unchecked") @Deprecated /** @deprecated since 0.7.0 use Function */
         public NamedActionWithUrl(String actionName, Closure<String> postProcessing) {
             this.actionName = actionName;
-            this.postProcessing = (postProcessing == null) ? null : GroovyJavaMethods.functionFromClosure(postProcessing);
+            this.postProcessing = (Function<T, String>) ((postProcessing == null) ? null : GroovyJavaMethods.functionFromClosure(postProcessing));
         }
 
-        public NamedActionWithUrl(String actionName, Function<Object, String> postProcessing) {
+        public NamedActionWithUrl(String actionName, Function<T, String> postProcessing) {
             this.actionName = actionName;
             this.postProcessing = postProcessing;
         }
 
-        public String getUrl(Entity e, AttributeSensor s) {
+        public String getUrl(Entity e, AttributeSensor<T> s) {
             return getUrlFromValue(e.getAttribute(s));
         }
 
@@ -111,14 +181,15 @@ public class RendererHints {
         }
 
         /** this is the method invoked by web console SensorSummary, at the moment */
-        public String getUrlFromValue(Object v) {
+        public String getUrlFromValue(T v) {
+            String v2;
             if (postProcessing != null) {
-                v = postProcessing.apply(v);
+                v2 = postProcessing.apply(v);
+            } else {
+                v2 = (v==null ? null : v.toString());
             }
-            if (v != null) {
-                return "" + v;
-            }
-            return null;
+            if (v2 == null) return v2;
+            return v2.toString();
         }
 
         @Override
@@ -129,7 +200,7 @@ public class RendererHints {
         @Override
         public boolean equals(Object obj) {
             if (!(obj instanceof NamedActionWithUrl)) return false;
-            NamedActionWithUrl o = (NamedActionWithUrl) obj;
+            NamedActionWithUrl<?> o = (NamedActionWithUrl<?>) obj;
             return Objects.equal(actionName, o.actionName) && Objects.equal(postProcessing, o.postProcessing);
         }
     }
@@ -142,15 +213,14 @@ public class RendererHints {
      * this functionality.</em>
      */
     @Beta
-    public static abstract class DisplayValue<T> extends Hint<T> {
+    public static class DisplayValue<T> extends Hint<T> {
         private final Function<Object, String> transform;
 
-        DisplayValue(Function<?, String> transform) {
+        @SuppressWarnings("unchecked")
+        protected DisplayValue(Function<?, String> transform) {
             this.transform = (Function<Object, String>) Preconditions.checkNotNull(transform, "transform");
         }
 
-        public abstract String getDisplayValue(Entity e, T v);
-
         public String getDisplayValue(Object v) {
             String dv = transform.apply(v);
             return Strings.nullToEmpty(dv);
@@ -164,60 +234,53 @@ public class RendererHints {
         @Override
         public boolean equals(Object obj) {
             if (obj == null || !(obj instanceof DisplayValue)) return false;
-            DisplayValue o = (DisplayValue) obj;
-            return Objects.equal(transform, o.transform);
+            return Objects.equal(transform, ((DisplayValue<?>)obj).transform);
         }
     }
 
     @Beta
-    public static class AttributeDisplayValue extends DisplayValue<AttributeSensor<?>> {
-        AttributeDisplayValue(Function<?, String> transform) {
-            super(transform);
-        }
-        public String getDisplayValue(Entity e, AttributeSensor<?> sensor) {
-            return getDisplayValue(e.getAttribute(sensor));
-        }
+    public static <T> DisplayValue<T> displayValue(Function<T, String> transform) {
+        return new DisplayValue<T>(transform);
     }
 
     @Beta
-    public static class ConfigKeyDisplayValue extends DisplayValue<ConfigKey<?>> {
-        ConfigKeyDisplayValue(Function<?, String> transform) {
-            super(transform);
-        }
-        public String getDisplayValue(Entity e, ConfigKey<?> configKey) {
-            return getDisplayValue(e.getConfig(configKey));
-        }
+    public static <T> NamedActionWithUrl<T> namedActionWithUrl(String actionName, Function<T, String> transform) {
+        return new RendererHints.NamedActionWithUrl<T>(actionName, transform);
     }
 
-    /** @deprecated Since 0.7.0 use attributeDisplayValue or configKeyDisplay value as appropriate. */
-    @Deprecated
     @Beta
-    public static DisplayValue<AttributeSensor<?>> displayValue(Function<?, String> transform) {
-        return new AttributeDisplayValue(transform);
+    public static <T> NamedActionWithUrl<T> namedActionWithUrl(String actionName) {
+        return new RendererHints.NamedActionWithUrl<T>(actionName);
     }
 
     @Beta
-    public static DisplayValue<AttributeSensor<?>> attributeDisplayValue(Function<?, String> transform) {
-        return new AttributeDisplayValue(transform);
+    public static <T> NamedActionWithUrl<T> namedActionWithUrl(Function<T, String> transform) {
+        return namedActionWithUrl("Open", transform);
     }
 
     @Beta
-    public static DisplayValue<ConfigKey<?>> configKeyDisplayValue(Function<?, String> transform) {
-        return new ConfigKeyDisplayValue(transform);
+    public static <T> NamedActionWithUrl<T> namedActionWithUrl() {
+        return namedActionWithUrl((Function<T,String>)null);
     }
 
     @Beta
-    public static NamedActionWithUrl namedActionWithUrl(String actionName, Function<Object, String> transform) {
-        return new RendererHints.NamedActionWithUrl(actionName, transform);
+    public static <T> NamedActionWithUrl<T> openWithUrl(Function<T, String> transform) {
+        return new RendererHints.NamedActionWithUrl<T>("Open", transform);
     }
 
     @Beta
-    public static NamedActionWithUrl openWithUrl(Function<Object, String> transform) {
-        return new RendererHints.NamedActionWithUrl("Open", transform);
+    public static <T> DisplayValue<T> censoredValue() {
+        return new DisplayValue<T>(Functions.constant("********"));
     }
-
-    @Beta
-    public static DisplayValue<ConfigKey<?>> censoredConfigKey() {
-        return new ConfigKeyDisplayValue(Functions.constant("********"));
+    
+    static {
+        // apply display hints for common utility objects
+        register(Duration.class, displayValue(Time.fromDurationToTimeStringRounded()));
+        register(HostAndPort.class, displayValue(StringFunctions.toStringFunction()));
+        register(UserAndHostAndPort.class, displayValue(StringFunctions.toStringFunction()));
+        
+        register(URL.class, displayValue(StringFunctions.toStringFunction()));
+        register(URL.class, openWithUrl(StringFunctions.toStringFunction()));
     }
+    
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/core/src/main/java/brooklyn/entity/basic/AbstractEntity.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/AbstractEntity.java b/core/src/main/java/brooklyn/entity/basic/AbstractEntity.java
index e1429e9..2d3fa58 100644
--- a/core/src/main/java/brooklyn/entity/basic/AbstractEntity.java
+++ b/core/src/main/java/brooklyn/entity/basic/AbstractEntity.java
@@ -33,6 +33,7 @@ import org.slf4j.LoggerFactory;
 import brooklyn.basic.AbstractBrooklynObject;
 import brooklyn.config.ConfigKey;
 import brooklyn.config.ConfigKey.HasConfigKey;
+import brooklyn.config.render.RendererHints;
 import brooklyn.enricher.basic.AbstractEnricher;
 import brooklyn.entity.Application;
 import brooklyn.entity.Effector;
@@ -157,6 +158,10 @@ public abstract class AbstractEntity extends AbstractBrooklynObject implements E
     public static final BasicNotificationSensor<Entity> CHILD_REMOVED = new BasicNotificationSensor<Entity>(Entity.class,
             "entity.children.removed", "Child dynamically removed from entity");
 
+    static {
+        RendererHints.register(Entity.class, RendererHints.displayValue(EntityFunctions.displayName()));
+    }
+        
     private boolean displayNameAutoGenerated = true;
     
     private Entity selfProxy;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/core/src/main/java/brooklyn/entity/basic/DelegateEntity.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/DelegateEntity.java b/core/src/main/java/brooklyn/entity/basic/DelegateEntity.java
index f4208b5..d41d1df 100644
--- a/core/src/main/java/brooklyn/entity/basic/DelegateEntity.java
+++ b/core/src/main/java/brooklyn/entity/basic/DelegateEntity.java
@@ -20,10 +20,6 @@ package brooklyn.entity.basic;
 
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import javax.annotation.Nullable;
-
-import com.google.common.base.Function;
-
 import brooklyn.config.render.RendererHints;
 import brooklyn.entity.Entity;
 import brooklyn.entity.Group;
@@ -32,6 +28,8 @@ import brooklyn.event.AttributeSensor;
 import brooklyn.event.basic.AttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
 
+import com.google.common.base.Function;
+
 /**
  * A delegate entity for use as a {@link Group} child proxy for members.
  */
@@ -46,28 +44,24 @@ public interface DelegateEntity extends Entity {
     public static class EntityUrl {
 
         private static final AtomicBoolean initialized = new AtomicBoolean(false);
-        private static final Function<Object, String> entityUrlFunction = new Function<Object, String>() {
+        private static final Function<Entity, String> entityUrlFunction = new Function<Entity, String>() {
             @Override
-            public String apply(Object input) {
-                if (input instanceof Entity) {
-                    Entity entity = (Entity) input;
-                    String url = String.format("#/v1/applications/%s/entities/%s", entity.getApplicationId(), entity.getId());
-                    return url;
-                } else {
-                    return null;
-                }
+            public String apply(Entity input) {
+                if (input==null) return null;
+                Entity entity = (Entity) input;
+                String url = String.format("#/v1/applications/%s/entities/%s", entity.getApplicationId(), entity.getId());
+                return url;
             }
         };
 
-        public static Function<Object, String> entityUrl() { return entityUrlFunction; }
+        public static Function<Entity, String> entityUrl() { return entityUrlFunction; }
 
         /** Setup renderer hints. */
-        @SuppressWarnings("rawtypes")
         public static void init() {
             if (initialized.getAndSet(true)) return;
 
-            RendererHints.register(DELEGATE_ENTITY, new RendererHints.NamedActionWithUrl("Open", entityUrl()));
-            RendererHints.register(DELEGATE_ENTITY_LINK, new RendererHints.NamedActionWithUrl("Open"));
+            RendererHints.register(DELEGATE_ENTITY, RendererHints.namedActionWithUrl(entityUrl()));
+            RendererHints.register(DELEGATE_ENTITY_LINK, RendererHints.namedActionWithUrl());
         }
 
         static {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/core/src/main/java/brooklyn/entity/basic/EntityPredicates.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/EntityPredicates.java b/core/src/main/java/brooklyn/entity/basic/EntityPredicates.java
index 367bc98..e0db9e9 100644
--- a/core/src/main/java/brooklyn/entity/basic/EntityPredicates.java
+++ b/core/src/main/java/brooklyn/entity/basic/EntityPredicates.java
@@ -34,6 +34,8 @@ import com.google.common.base.Predicate;
 @SuppressWarnings("serial")
 public class EntityPredicates {
 
+    // TODO convert these to named classes. but keep the anonymous ones around for deserialization purposes!
+    
     public static <T> Predicate<Entity> idEqualTo(final T val) {
         return new SerializablePredicate<Entity>() {
             @Override

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/core/src/main/java/brooklyn/entity/basic/Lifecycle.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/Lifecycle.java b/core/src/main/java/brooklyn/entity/basic/Lifecycle.java
index cf278c9..89e469f 100644
--- a/core/src/main/java/brooklyn/entity/basic/Lifecycle.java
+++ b/core/src/main/java/brooklyn/entity/basic/Lifecycle.java
@@ -23,7 +23,11 @@ import java.util.Date;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import brooklyn.config.render.RendererHints;
 import brooklyn.util.flags.TypeCoercions;
+import brooklyn.util.text.StringFunctions;
+import brooklyn.util.time.Duration;
+import brooklyn.util.time.Time;
 
 import com.google.common.base.CaseFormat;
 import com.google.common.base.Function;
@@ -176,6 +180,7 @@ public enum Lifecycle {
     }
 
     static {
-        TypeCoercions.registerAdapter(String.class, Transition.class, new TransitionCoalesceFunction()); 
+        TypeCoercions.registerAdapter(String.class, Transition.class, new TransitionCoalesceFunction());
+        RendererHints.register(Transition.class, RendererHints.displayValue(StringFunctions.toStringFunction()));
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/core/src/main/java/brooklyn/entity/group/DynamicClusterImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/group/DynamicClusterImpl.java b/core/src/main/java/brooklyn/entity/group/DynamicClusterImpl.java
index 3c1bc82..b37f0be 100644
--- a/core/src/main/java/brooklyn/entity/group/DynamicClusterImpl.java
+++ b/core/src/main/java/brooklyn/entity/group/DynamicClusterImpl.java
@@ -116,8 +116,8 @@ public class DynamicClusterImpl extends AbstractGroupImpl implements DynamicClus
     }
 
     static {
-        RendererHints.register(FIRST, new RendererHints.NamedActionWithUrl("Open", DelegateEntity.EntityUrl.entityUrl()));
-        RendererHints.register(CLUSTER, new RendererHints.NamedActionWithUrl("Open", DelegateEntity.EntityUrl.entityUrl()));
+        RendererHints.register(FIRST, RendererHints.namedActionWithUrl("Open", DelegateEntity.EntityUrl.entityUrl()));
+        RendererHints.register(CLUSTER, RendererHints.namedActionWithUrl("Open", DelegateEntity.EntityUrl.entityUrl()));
     }
 
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeImpl.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeImpl.java
index 186b5c6..4dca1ea 100644
--- a/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeImpl.java
+++ b/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeImpl.java
@@ -64,7 +64,7 @@ public class BrooklynNodeImpl extends SoftwareProcessImpl implements BrooklynNod
     private static final Logger log = LoggerFactory.getLogger(BrooklynNodeImpl.class);
 
     static {
-        RendererHints.register(WEB_CONSOLE_URI, new RendererHints.NamedActionWithUrl("Open"));
+        RendererHints.register(WEB_CONSOLE_URI, RendererHints.namedActionWithUrl());
     }
 
     private HttpFeed httpFeed;
@@ -85,7 +85,7 @@ public class BrooklynNodeImpl extends SoftwareProcessImpl implements BrooklynNod
     @Override
     public void init() {
         super.init();
-        RendererHints.register(MANAGEMENT_PASSWORD, RendererHints.censoredConfigKey());
+        RendererHints.register(MANAGEMENT_PASSWORD, RendererHints.censoredValue());
         getMutableEntityType().addEffector(DeployBlueprintEffectorBody.DEPLOY_BLUEPRINT);
         getMutableEntityType().addEffector(ShutdownEffectorBody.SHUTDOWN);
         getMutableEntityType().addEffector(StopNodeButLeaveAppsEffectorBody.STOP_NODE_BUT_LEAVE_APPS);
@@ -124,6 +124,8 @@ public class BrooklynNodeImpl extends SoftwareProcessImpl implements BrooklynNod
         
         @Override
         public String call(ConfigBag parameters) {
+            if (log.isDebugEnabled())
+                log.debug("Deploying blueprint to "+entity()+": "+parameters);
             String plan = extractPlanYamlString(parameters);
             return submitPlan(plan);
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/software/base/src/main/java/brooklyn/entity/machine/MachineAttributes.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/machine/MachineAttributes.java b/software/base/src/main/java/brooklyn/entity/machine/MachineAttributes.java
index 3a6b849..83ab05f 100644
--- a/software/base/src/main/java/brooklyn/entity/machine/MachineAttributes.java
+++ b/software/base/src/main/java/brooklyn/entity/machine/MachineAttributes.java
@@ -29,7 +29,6 @@ import brooklyn.util.guava.Functionals;
 import brooklyn.util.math.MathFunctions;
 import brooklyn.util.text.ByteSizeStrings;
 import brooklyn.util.time.Duration;
-import brooklyn.util.time.Time;
 
 import com.google.common.base.Function;
 
@@ -72,8 +71,6 @@ public class MachineAttributes {
             }
         };
 
-        RendererHints.register(UPTIME, RendererHints.displayValue(Time.toTimeStringRounded()));
-
         RendererHints.register(CPU_USAGE, RendererHints.displayValue(MathFunctions.percent(2)));
         RendererHints.register(AVERAGE_CPU_USAGE, RendererHints.displayValue(MathFunctions.percent(2)));
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/software/messaging/src/main/java/brooklyn/entity/messaging/storm/Storm.java
----------------------------------------------------------------------
diff --git a/software/messaging/src/main/java/brooklyn/entity/messaging/storm/Storm.java b/software/messaging/src/main/java/brooklyn/entity/messaging/storm/Storm.java
index 455747a..ac9498e 100644
--- a/software/messaging/src/main/java/brooklyn/entity/messaging/storm/Storm.java
+++ b/software/messaging/src/main/java/brooklyn/entity/messaging/storm/Storm.java
@@ -97,7 +97,7 @@ public interface Storm extends SoftwareProcess, UsesJmx {
         public static final AttributeSensor<String> STORM_UI_URL = Sensors.newStringSensor("storm.ui.url", "URL");
 
         static {
-            RendererHints.register(STORM_UI_URL, new RendererHints.NamedActionWithUrl("Open"));
+            RendererHints.register(STORM_UI_URL, RendererHints.namedActionWithUrl());
         }
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNode.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNode.java b/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNode.java
index 4a843dd..93f6ef6 100644
--- a/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNode.java
+++ b/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNode.java
@@ -109,7 +109,7 @@ public interface CouchbaseNode extends SoftwareProcess {
         
         static {
             // ROOT_URL does not need init because it refers to something already initialized
-            RendererHints.register(COUCHBASE_WEB_ADMIN_URL, new RendererHints.NamedActionWithUrl("Open"));
+            RendererHints.register(COUCHBASE_WEB_ADMIN_URL, RendererHints.namedActionWithUrl());
 
             RendererHints.register(COUCH_DOCS_DATA_SIZE, RendererHints.displayValue(ByteSizeStrings.metric()));
             RendererHints.register(COUCH_DOCS_ACTUAL_DISK_SIZE, RendererHints.displayValue(ByteSizeStrings.metric()));

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseSyncGatewayImpl.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseSyncGatewayImpl.java b/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseSyncGatewayImpl.java
index 4ac3400..ae82af4 100644
--- a/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseSyncGatewayImpl.java
+++ b/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseSyncGatewayImpl.java
@@ -77,6 +77,6 @@ public class CouchbaseSyncGatewayImpl extends SoftwareProcessImpl implements Cou
     }
     
     static {
-        RendererHints.register(MANAGEMENT_URL, new RendererHints.NamedActionWithUrl("Open"));
+        RendererHints.register(MANAGEMENT_URL, RendererHints.namedActionWithUrl());
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/software/nosql/src/main/java/brooklyn/entity/nosql/mongodb/MongoDBServerImpl.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/brooklyn/entity/nosql/mongodb/MongoDBServerImpl.java b/software/nosql/src/main/java/brooklyn/entity/nosql/mongodb/MongoDBServerImpl.java
index c47f58b..e1e6c5f 100644
--- a/software/nosql/src/main/java/brooklyn/entity/nosql/mongodb/MongoDBServerImpl.java
+++ b/software/nosql/src/main/java/brooklyn/entity/nosql/mongodb/MongoDBServerImpl.java
@@ -41,7 +41,7 @@ public class MongoDBServerImpl extends SoftwareProcessImpl implements MongoDBSer
     private static final Logger LOG = LoggerFactory.getLogger(MongoDBServerImpl.class);
 
     static {
-        RendererHints.register(HTTP_INTERFACE_URL, new RendererHints.NamedActionWithUrl("Open"));
+        RendererHints.register(HTTP_INTERFACE_URL, RendererHints.namedActionWithUrl());
     }
 
     private FunctionFeed serviceStats;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/software/webapp/src/main/java/brooklyn/entity/webapp/WebAppServiceConstants.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/brooklyn/entity/webapp/WebAppServiceConstants.java b/software/webapp/src/main/java/brooklyn/entity/webapp/WebAppServiceConstants.java
index 4a9903e..dbb9ec5 100644
--- a/software/webapp/src/main/java/brooklyn/entity/webapp/WebAppServiceConstants.java
+++ b/software/webapp/src/main/java/brooklyn/entity/webapp/WebAppServiceConstants.java
@@ -56,6 +56,6 @@ class RootUrl {
     public static final AttributeSensor<String> ROOT_URL = Sensors.newStringSensor("webapp.url", "URL");
 
     static {
-        RendererHints.register(ROOT_URL, new RendererHints.NamedActionWithUrl("Open"));
+        RendererHints.register(ROOT_URL, RendererHints.namedActionWithUrl());
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/software/webapp/src/main/java/brooklyn/entity/webapp/WebAppServiceMetrics.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/brooklyn/entity/webapp/WebAppServiceMetrics.java b/software/webapp/src/main/java/brooklyn/entity/webapp/WebAppServiceMetrics.java
index 710e344..c29be64 100644
--- a/software/webapp/src/main/java/brooklyn/entity/webapp/WebAppServiceMetrics.java
+++ b/software/webapp/src/main/java/brooklyn/entity/webapp/WebAppServiceMetrics.java
@@ -22,13 +22,12 @@ import brooklyn.config.render.RendererHints;
 import brooklyn.event.AttributeSensor;
 import brooklyn.event.basic.BasicAttributeSensor;
 import brooklyn.event.basic.Sensors;
+import brooklyn.util.math.MathFunctions;
 import brooklyn.util.text.ByteSizeStrings;
 import brooklyn.util.time.Duration;
 
 public interface WebAppServiceMetrics {
     
-    public static final AttributeSensor<Integer> REQUEST_COUNT = Initializer.REQUEST_COUNT;
-        
     public static final brooklyn.event.basic.BasicAttributeSensor<Integer> ERROR_COUNT =
             new brooklyn.event.basic.BasicAttributeSensor<Integer>(Integer.class, "webapp.reqs.errors", "Request errors");
     public static final AttributeSensor<Integer> TOTAL_PROCESSING_TIME = Sensors.newIntegerSensor(
@@ -57,7 +56,10 @@ public interface WebAppServiceMetrics {
     public static final AttributeSensor<Double> REQUESTS_PER_SECOND_IN_WINDOW =
             Sensors.newDoubleSensor("webapp.reqs.perSec.windowed", "Reqs/sec (over time window)");
 
+    public static final AttributeSensor<Integer> REQUEST_COUNT = Initializer.REQUEST_COUNT;
+
     // this class is added because the above need static initialization which unfortunately can't be added to an interface.
+    // (but should only be referenced after the other fields have been set)
     static class Initializer {
         public static final AttributeSensor<Integer> REQUEST_COUNT =
             Sensors.newIntegerSensor("webapp.reqs.total", "Request count");
@@ -67,6 +69,8 @@ public interface WebAppServiceMetrics {
             RendererHints.register(WebAppServiceConstants.MAX_PROCESSING_TIME, RendererHints.displayValue(Duration.millisToStringRounded()));
             RendererHints.register(WebAppServiceConstants.BYTES_RECEIVED, RendererHints.displayValue(ByteSizeStrings.metric()));
             RendererHints.register(WebAppServiceConstants.BYTES_SENT, RendererHints.displayValue(ByteSizeStrings.metric()));
+            RendererHints.register(WebAppServiceConstants.PROCESSING_TIME_FRACTION_LAST, RendererHints.displayValue(MathFunctions.percent(2)));
+            RendererHints.register(WebAppServiceConstants.PROCESSING_TIME_FRACTION_IN_WINDOW, RendererHints.displayValue(MathFunctions.percent(2)));
         }
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/software/webapp/src/main/java/brooklyn/entity/webapp/jboss/JBoss7Server.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/brooklyn/entity/webapp/jboss/JBoss7Server.java b/software/webapp/src/main/java/brooklyn/entity/webapp/jboss/JBoss7Server.java
index 70b5131..ae9c24e 100644
--- a/software/webapp/src/main/java/brooklyn/entity/webapp/jboss/JBoss7Server.java
+++ b/software/webapp/src/main/java/brooklyn/entity/webapp/jboss/JBoss7Server.java
@@ -100,4 +100,5 @@ public interface JBoss7Server extends JavaWebAppSoftwareProcess, HasShortName {
             Sensors.newBooleanSensor("webapp.jboss.managementUp", "Management server is responding with OK");
     
     public static final AttributeSensor<String> PID_FILE = Sensors.newStringSensor( "jboss.pid.file", "PID file");
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/software/webapp/src/main/java/brooklyn/entity/webapp/jboss/JBoss7ServerImpl.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/brooklyn/entity/webapp/jboss/JBoss7ServerImpl.java b/software/webapp/src/main/java/brooklyn/entity/webapp/jboss/JBoss7ServerImpl.java
index 3c8e464..9ab0038 100644
--- a/software/webapp/src/main/java/brooklyn/entity/webapp/jboss/JBoss7ServerImpl.java
+++ b/software/webapp/src/main/java/brooklyn/entity/webapp/jboss/JBoss7ServerImpl.java
@@ -23,6 +23,7 @@ import java.util.Map;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import brooklyn.config.render.RendererHints;
 import brooklyn.enricher.Enrichers;
 import brooklyn.entity.Entity;
 import brooklyn.entity.basic.Attributes;
@@ -67,6 +68,10 @@ public class JBoss7ServerImpl extends JavaWebAppSoftwareProcessImpl implements J
         return (JBoss7Driver) super.getDriver();
     }
     
+    static {
+        RendererHints.register(MANAGEMENT_URL, RendererHints.namedActionWithUrl());
+    }
+
     @Override
     protected void connectSensors() {
         super.connectSensors();
@@ -104,6 +109,7 @@ public class JBoss7ServerImpl extends JavaWebAppSoftwareProcessImpl implements J
                         .onSuccess(HttpValueFunctions.jsonContents("maxTime", Integer.class)))
                 .poll(new HttpPollConfig<Long>(BYTES_RECEIVED)
                         .vars(includeRuntimeUriVars)
+                        // jboss seems to report 0 even if it has received lots of requests; dunno why.
                         .onSuccess(HttpValueFunctions.jsonContents("bytesReceived", Long.class)))
                 .poll(new HttpPollConfig<Long>(BYTES_SENT)
                         .vars(includeRuntimeUriVars)

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/software/webapp/src/main/java/brooklyn/entity/webapp/jboss/JBoss7SshDriver.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/brooklyn/entity/webapp/jboss/JBoss7SshDriver.java b/software/webapp/src/main/java/brooklyn/entity/webapp/jboss/JBoss7SshDriver.java
index 5b8a036..316cf6d 100644
--- a/software/webapp/src/main/java/brooklyn/entity/webapp/jboss/JBoss7SshDriver.java
+++ b/software/webapp/src/main/java/brooklyn/entity/webapp/jboss/JBoss7SshDriver.java
@@ -24,7 +24,6 @@ import java.io.InputStream;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.UUID;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -147,7 +146,7 @@ public class JBoss7SshDriver extends JavaWebAppSshDriver implements JBoss7Driver
         String managementPassword = getManagementPassword();
         if (Strings.isBlank(managementPassword)) {
             LOG.debug(this+" has no password specified for "+JBoss7Server.MANAGEMENT_PASSWORD.getName()+"; using a random string");
-            entity.setConfig(JBoss7Server.MANAGEMENT_PASSWORD, UUID.randomUUID().toString());
+            entity.setConfig(JBoss7Server.MANAGEMENT_PASSWORD, Strings.makeRandomId(8));
         }
         String hashedPassword = hashPassword(getManagementUsername(), getManagementPassword(), MANAGEMENT_REALM);
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/usage/rest-server/src/main/java/brooklyn/rest/resources/EntityConfigResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/resources/EntityConfigResource.java b/usage/rest-server/src/main/java/brooklyn/rest/resources/EntityConfigResource.java
index 9cf484e..8df88d1 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/resources/EntityConfigResource.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/resources/EntityConfigResource.java
@@ -32,7 +32,6 @@ import brooklyn.entity.Entity;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.basic.EntityLocal;
-import brooklyn.event.AttributeSensor;
 import brooklyn.event.basic.BasicConfigKey;
 import brooklyn.management.entitlement.Entitlements;
 import brooklyn.rest.api.EntityConfigApi;
@@ -81,13 +80,14 @@ public class EntityConfigResource extends AbstractBrooklynRestResource implement
         return result;
     }
 
+    @SuppressWarnings("rawtypes")
     public static Object applyDisplayValueHint(ConfigKey<?> configKey, Object value) {
-        Iterable<RendererHints.ConfigKeyDisplayValue> hints = Iterables.filter(RendererHints.getHintsFor(configKey), RendererHints.ConfigKeyDisplayValue.class);
+        Iterable<RendererHints.DisplayValue> hints = Iterables.filter(RendererHints.getHintsFor(configKey), RendererHints.DisplayValue.class);
         if (Iterables.size(hints) > 1) {
             LOG.warn("Multiple display value hints set for sensor {}; Only one will be applied, using first", configKey);
         }
 
-        Optional<RendererHints.ConfigKeyDisplayValue> hint = Optional.fromNullable(Iterables.getFirst(hints, null));
+        Optional<RendererHints.DisplayValue> hint = Optional.fromNullable(Iterables.getFirst(hints, null));
         return hint.isPresent() ? hint.get().getDisplayValue(value) : value;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/usage/rest-server/src/main/java/brooklyn/rest/resources/SensorResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/resources/SensorResource.java b/usage/rest-server/src/main/java/brooklyn/rest/resources/SensorResource.java
index df1125c..3981b95 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/resources/SensorResource.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/resources/SensorResource.java
@@ -40,8 +40,6 @@ import brooklyn.rest.transform.SensorTransformer;
 import brooklyn.rest.util.WebResourceUtils;
 
 import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
@@ -73,7 +71,7 @@ public class SensorResource extends AbstractBrooklynRestResource implements Sens
         for (AttributeSensor<?> sensor : sensors) {
             Object value = entity.getAttribute(findSensor(entity, sensor.getName()));
             if (Boolean.FALSE.equals(raw)) {
-                value = applyDisplayValueHint(sensor, value);
+                value = RendererHints.applyDisplayValueHint(sensor, value);
             }
             sensorMap.put(sensor.getName(), getValueForDisplay(value, true, false));
         }
@@ -85,7 +83,7 @@ public class SensorResource extends AbstractBrooklynRestResource implements Sens
         AttributeSensor<?> sensor = findSensor(entity, sensorName);
         Object value = entity.getAttribute(sensor);
         if (Boolean.FALSE.equals(raw)) {
-            value = applyDisplayValueHint(sensor, value);
+            value = RendererHints.applyDisplayValueHint(sensor, value);
         }
         return getValueForDisplay(value, preferJson, true);
     }
@@ -100,16 +98,6 @@ public class SensorResource extends AbstractBrooklynRestResource implements Sens
         return get(true, application, entityToken, sensorName, raw);
     }
 
-    public static Object applyDisplayValueHint(AttributeSensor<?> sensor, Object initialValue) {
-        Iterable<RendererHints.AttributeDisplayValue> hints = Iterables.filter(RendererHints.getHintsFor(sensor), RendererHints.AttributeDisplayValue.class);
-        if (Iterables.size(hints) > 1) {
-            log.warn("Multiple display value hints set for sensor {}; Only one will be applied, using first", sensor);
-        }
-
-        Optional<RendererHints.AttributeDisplayValue> hint = Optional.fromNullable(Iterables.getFirst(hints, null));
-        return hint.isPresent() ? hint.get().getDisplayValue(initialValue) : initialValue;
-    }
-
     private AttributeSensor<?> findSensor(EntityLocal entity, String name) {
         Sensor<?> s = entity.getEntityType().getSensor(name);
         if (s instanceof AttributeSensor) return (AttributeSensor<?>) s;
@@ -126,6 +114,8 @@ public class SensorResource extends AbstractBrooklynRestResource implements Sens
         }
         
         AttributeSensor sensor = findSensor(entity, sensorName);
+        if (log.isDebugEnabled())
+            log.debug("REST user "+Entitlements.getEntitlementContext()+" setting sensor "+sensorName+" to "+newValue);
         entity.setAttribute(sensor, newValue);
     }
     
@@ -133,6 +123,8 @@ public class SensorResource extends AbstractBrooklynRestResource implements Sens
     public void delete(String application, String entityToken, String sensorName) {
         final EntityLocal entity = brooklyn().getEntity(application, entityToken);
         AttributeSensor<?> sensor = findSensor(entity, sensorName);
+        if (log.isDebugEnabled())
+            log.debug("REST user "+Entitlements.getEntitlementContext().user()+" deleting sensor "+sensorName);
         ((EntityInternal)entity).removeAttribute(sensor);
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java b/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java
index 824b2ad..56176cf 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java
@@ -23,7 +23,6 @@ import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Properties;
-import java.util.Timer;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -31,8 +30,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Preconditions;
-
 import brooklyn.BrooklynVersion;
 import brooklyn.entity.Application;
 import brooklyn.entity.basic.Entities;
@@ -55,6 +52,8 @@ import brooklyn.util.time.CountdownTimer;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;
 
+import com.google.common.base.Preconditions;
+
 public class ServerResource extends AbstractBrooklynRestResource implements ServerApi {
 
     private static final int SHUTDOWN_TIMEOUT_CHECK_INTERVAL = 200;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/usage/rest-server/src/main/java/brooklyn/rest/transform/SensorTransformer.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/transform/SensorTransformer.java b/usage/rest-server/src/main/java/brooklyn/rest/transform/SensorTransformer.java
index dbaa860..ab9618c 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/transform/SensorTransformer.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/transform/SensorTransformer.java
@@ -43,7 +43,6 @@ public class SensorTransformer {
                 sensor.getDescription(), null);
     }
 
-    @SuppressWarnings("rawtypes")
     public static SensorSummary sensorSummary(Entity entity, Sensor<?> sensor) {
         String applicationUri = "/v1/applications/" + entity.getApplicationId();
         String entityUri = applicationUri + "/entities/" + entity.getId();
@@ -55,24 +54,27 @@ public class SensorTransformer {
                 .put("entity", URI.create(entityUri))
                 .put("action:json", URI.create(selfUri));
 
-        Iterable<RendererHints.NamedAction> hints = Iterables.filter(RendererHints.getHintsFor(sensor), RendererHints.NamedAction.class);
-        for (RendererHints.NamedAction na : hints) addNamedAction(lb, na , entity, sensor);
+        if (sensor instanceof AttributeSensor) {
+            Iterable<RendererHints.NamedAction> hints = Iterables.filter(RendererHints.getHintsFor((AttributeSensor<?>)sensor), RendererHints.NamedAction.class);
+            for (RendererHints.NamedAction na : hints) addNamedAction(lb, na , entity, sensor);
+        }
 
         return new SensorSummary(sensor.getName(), sensor.getTypeName(), sensor.getDescription(), lb.build());
     }
 
-    @SuppressWarnings("rawtypes")
-    private static void addNamedAction(MutableMap.Builder<String, URI> lb, RendererHints.NamedAction na , Entity entity, Sensor<?> sensor) {
+    // TODO should do this with config also (e.g. to show entities or URLs as clickable links); but we need to set up a ConfigTransformer
+    @SuppressWarnings("unchecked")
+    private static <T> void addNamedAction(MutableMap.Builder<String, URI> lb, RendererHints.NamedAction na , Entity entity, Sensor<T> sensor) {
         if (na instanceof RendererHints.NamedActionWithUrl) {
             try {
-                String v = ((RendererHints.NamedActionWithUrl) na).getUrl(entity, (AttributeSensor<?>) sensor);
+                String v = ((RendererHints.NamedActionWithUrl<T>) na).getUrl(entity, (AttributeSensor<T>) sensor);
                 if (Strings.isNonBlank(v)) {
                     String action = na.getActionName().toLowerCase();
                     lb.putIfAbsent("action:"+action, URI.create(v));
                 }
             } catch (Exception e) {
                 Exceptions.propagateIfFatal(e);
-                log.warn("Unable to make use of URL sensor "+sensor+" on "+entity+": "+e);
+                log.warn("Unable to make use of URL sensor "+sensor+" on "+entity+": "+e, e);
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/usage/rest-server/src/main/java/brooklyn/rest/util/json/ErrorAndToStringUnknownTypeSerializer.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/util/json/ErrorAndToStringUnknownTypeSerializer.java b/usage/rest-server/src/main/java/brooklyn/rest/util/json/ErrorAndToStringUnknownTypeSerializer.java
index 287ebd3..f4afbd5 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/util/json/ErrorAndToStringUnknownTypeSerializer.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/util/json/ErrorAndToStringUnknownTypeSerializer.java
@@ -51,7 +51,7 @@ public class ErrorAndToStringUnknownTypeSerializer extends UnknownSerializer {
     @Override
     public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
         if (BidiSerialization.isStrictSerialization())
-            throw new JsonMappingException("Cannot containing "+value.getClass().getName()+" and strict serialization requested");
+            throw new JsonMappingException("Cannot serialize object containing "+value.getClass().getName()+" when strict serialization requested");
 
         serializeFromError(jgen.getOutputContext(), null, value, jgen, provider);
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/usage/rest-server/src/test/java/brooklyn/rest/domain/SensorSummaryTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/brooklyn/rest/domain/SensorSummaryTest.java b/usage/rest-server/src/test/java/brooklyn/rest/domain/SensorSummaryTest.java
index 2f52c3c..a524966 100644
--- a/usage/rest-server/src/test/java/brooklyn/rest/domain/SensorSummaryTest.java
+++ b/usage/rest-server/src/test/java/brooklyn/rest/domain/SensorSummaryTest.java
@@ -92,8 +92,8 @@ public class SensorSummaryTest {
   public void testSensorWithMultipleOpenUrlActionsRegistered() throws IOException {
       AttributeSensor<String> sensor = Sensors.newStringSensor("sensor1");
       entity.setAttribute(sensor, "http://myval");
-      RendererHints.register(sensor, new RendererHints.NamedActionWithUrl("Open"));
-      RendererHints.register(sensor, new RendererHints.NamedActionWithUrl("Open"));
+      RendererHints.register(sensor, RendererHints.namedActionWithUrl());
+      RendererHints.register(sensor, RendererHints.namedActionWithUrl());
 
       SensorSummary summary = SensorTransformer.sensorSummary(entity, sensor);
       

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b567f1a5/utils/common/src/main/java/brooklyn/util/time/Time.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/util/time/Time.java b/utils/common/src/main/java/brooklyn/util/time/Time.java
index 27032d9..7fb5e1b 100644
--- a/utils/common/src/main/java/brooklyn/util/time/Time.java
+++ b/utils/common/src/main/java/brooklyn/util/time/Time.java
@@ -37,7 +37,7 @@ import com.google.common.base.Stopwatch;
 
 public class Time {
 
-	public static final String DATE_FORMAT_PREFERRED = "yyyy-MM-dd HH:mm:ss.SSS";
+    public static final String DATE_FORMAT_PREFERRED = "yyyy-MM-dd HH:mm:ss.SSS";
 	public static final String DATE_FORMAT_STAMP = "yyyyMMdd-HHmmssSSS";
 
 	public static final long MILLIS_IN_SECOND = 1000;
@@ -220,7 +220,19 @@ public class Time {
 		return Strings.removeAllFromEnd(result, " ");
 	}
 
+    public static Function<Long, String> fromLongToTimeStringExact() { return LONG_TO_TIME_STRING_EXACT; }
+    private static final Function<Long, String> LONG_TO_TIME_STRING_EXACT = new FunctionLongToTimeStringExact();
+    private static final class FunctionLongToTimeStringExact implements Function<Long, String> {
+        @Override @Nullable
+        public String apply(@Nullable Long input) {
+            if (input == null) return null;
+            return Time.makeTimeStringExact(input);
+        }
+    }
+
+    /** @deprecated since 0.7.0 use {@link #fromLongToTimeStringExact()} */ @Deprecated
     public static Function<Long, String> toTimeString() { return timeString; }
+    @Deprecated
     private static Function<Long, String> timeString = new Function<Long, String>() {
             @Override
             @Nullable
@@ -229,16 +241,38 @@ public class Time {
                 return Time.makeTimeStringExact(input);
             }
         };
+        
+    public static Function<Long, String> fromLongToTimeStringRounded() { return LONG_TO_TIME_STRING_ROUNDED; }
+    private static final Function<Long, String> LONG_TO_TIME_STRING_ROUNDED = new FunctionLongToTimeStringRounded();
+    private static final class FunctionLongToTimeStringRounded implements Function<Long, String> {
+        @Override @Nullable
+        public String apply(@Nullable Long input) {
+            if (input == null) return null;
+            return Time.makeTimeStringRounded(input);
+        }
+    }
 
+    /** @deprecated since 0.7.0 use {@link #fromLongToTimeStringRounded()} */ @Deprecated
     public static Function<Long, String> toTimeStringRounded() { return timeStringRounded; }
+    @Deprecated
     private static Function<Long, String> timeStringRounded = new Function<Long, String>() {
-            @Override
-            @Nullable
-            public String apply(@Nullable Long input) {
-                if (input == null) return null;
-                return Time.makeTimeStringRounded(input);
-            }
-        };
+        @Override
+        @Nullable
+        public String apply(@Nullable Long input) {
+            if (input == null) return null;
+            return Time.makeTimeStringRounded(input);
+        }
+    };
+
+    public static Function<Duration, String> fromDurationToTimeStringRounded() { return DURATION_TO_TIME_STRING_ROUNDED; }
+    private static final Function<Duration, String> DURATION_TO_TIME_STRING_ROUNDED = new FunctionDurationToTimeStringRounded();
+    private static final class FunctionDurationToTimeStringRounded implements Function<Duration, String> {
+        @Override @Nullable
+        public String apply(@Nullable Duration input) {
+            if (input == null) return null;
+            return Time.makeTimeStringRounded(input);
+        }
+    }
 
 	private static String toDecimal(long intPart, double fracPart, int decimalPrecision) {
 		long powTen = 1;


Mime
View raw message