http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/messaging/src/main/java/brooklyn/entity/messaging/jms/JMSBrokerImpl.java
----------------------------------------------------------------------
diff --git 
a/software/messaging/src/main/java/brooklyn/entity/messaging/jms/JMSBrokerImpl.java
 
b/software/messaging/src/main/java/brooklyn/entity/messaging/jms/JMSBrokerImpl.java
index 24c570b..fa69522 100644
--- 
a/software/messaging/src/main/java/brooklyn/entity/messaging/jms/JMSBrokerImpl.java
+++ 
b/software/messaging/src/main/java/brooklyn/entity/messaging/jms/JMSBrokerImpl.java
@@ -26,6 +26,7 @@ import java.util.Map;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import brooklyn.entity.basic.Lifecycle;
 import brooklyn.entity.basic.SoftwareProcessImpl;
 import brooklyn.entity.messaging.Queue;
 import brooklyn.entity.messaging.Topic;
@@ -132,9 +133,17 @@ public abstract class JMSBrokerImpl<Q extends 
JMSDestination & Queue, T extends
         addQueue(name, MutableMap.of());
     }
     
+    public void checkStartingOrRunning() {
+        Lifecycle state = getAttribute(SERVICE_STATE_ACTUAL);
+        if (getAttribute(SERVICE_STATE_ACTUAL) == Lifecycle.RUNNING) return;
+        if (getAttribute(SERVICE_STATE_ACTUAL) == Lifecycle.STARTING) return;
+        // TODO this check may be redundant or even inappropriate
+        throw new IllegalStateException("Cannot run against "+this+" in state 
"+state);
+    }
+
     @Override
     public void addQueue(String name, Map properties) {
-               checkModifiable();
+               checkStartingOrRunning();
         properties.put("name", name);
         queues.put(name, createQueue(properties));
     }
@@ -149,7 +158,7 @@ public abstract class JMSBrokerImpl<Q extends 
JMSDestination & Queue, T extends
     
     @Override
     public void addTopic(String name, Map properties) {
-               checkModifiable();
+               checkStartingOrRunning();
         properties.put("name", name);
         topics.put(name, createTopic(properties));
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/messaging/src/main/java/brooklyn/entity/zookeeper/ZooKeeperEnsembleImpl.java
----------------------------------------------------------------------
diff --git 
a/software/messaging/src/main/java/brooklyn/entity/zookeeper/ZooKeeperEnsembleImpl.java
 
b/software/messaging/src/main/java/brooklyn/entity/zookeeper/ZooKeeperEnsembleImpl.java
index 520e0af..511bb6b 100644
--- 
a/software/messaging/src/main/java/brooklyn/entity/zookeeper/ZooKeeperEnsembleImpl.java
+++ 
b/software/messaging/src/main/java/brooklyn/entity/zookeeper/ZooKeeperEnsembleImpl.java
@@ -79,26 +79,23 @@ public class ZooKeeperEnsembleImpl extends 
DynamicClusterImpl implements ZooKeep
             if (member.getAttribute(ZooKeeperNode.MY_ID) == null) {
                 ((EntityInternal) member).setAttribute(ZooKeeperNode.MY_ID, 
myId.incrementAndGet());
             }
-            entity.setAttribute(SERVICE_UP, 
((ZooKeeperEnsembleImpl)entity).calculateServiceUp());
         }
 
         @Override
         protected void onEntityRemoved(Entity member) {
-            entity.setAttribute(SERVICE_UP, 
((ZooKeeperEnsembleImpl)entity).calculateServiceUp());
         }
     };
 
     @Override
-    public synchronized boolean addMember(Entity member) {
-        boolean result = super.addMember(member);
-        setAttribute(SERVICE_UP, calculateServiceUp());
-        return result;
+    protected void initEnrichers() {
+        super.initEnrichers();
+        
     }
-
+    
     @Override
     public void start(Collection<? extends Location> locations) {
         super.start(locations);
-        setAttribute(Startable.SERVICE_UP, calculateServiceUp());
+        
         List<String> zookeeperServers = Lists.newArrayList();
         for (Entity zookeeper : getMembers()) {
             zookeeperServers.add(zookeeper.getAttribute(Attributes.HOSTNAME));
@@ -106,13 +103,4 @@ public class ZooKeeperEnsembleImpl extends 
DynamicClusterImpl implements ZooKeep
         setAttribute(ZOOKEEPER_SERVERS, zookeeperServers);
     }
 
-    @Override
-    protected boolean calculateServiceUp() {
-        boolean up = false;
-        for (Entity member : getMembers()) {
-            if (Boolean.TRUE.equals(member.getAttribute(SERVICE_UP))) up = 
true;
-        }
-        return up;
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/nosql/src/main/java/brooklyn/entity/nosql/cassandra/CassandraDatacenterImpl.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/main/java/brooklyn/entity/nosql/cassandra/CassandraDatacenterImpl.java
 
b/software/nosql/src/main/java/brooklyn/entity/nosql/cassandra/CassandraDatacenterImpl.java
index 9bf442b..b04e362 100644
--- 
a/software/nosql/src/main/java/brooklyn/entity/nosql/cassandra/CassandraDatacenterImpl.java
+++ 
b/software/nosql/src/main/java/brooklyn/entity/nosql/cassandra/CassandraDatacenterImpl.java
@@ -38,6 +38,7 @@ import brooklyn.entity.basic.DynamicGroup;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityPredicates;
 import brooklyn.entity.basic.Lifecycle;
+import brooklyn.entity.basic.ServiceStateLogic.ServiceNotUpLogic;
 import brooklyn.entity.effector.EffectorBody;
 import brooklyn.entity.group.AbstractMembershipTrackingPolicy;
 import brooklyn.entity.group.DynamicClusterImpl;
@@ -111,7 +112,7 @@ public class CassandraDatacenterImpl extends 
DynamicClusterImpl implements Cassa
                     return ImmutableSet.of();
                 } else if (hasPublishedSeeds) {
                     Set<Entity> currentSeeds = getAttribute(CURRENT_SEEDS);
-                    if (getAttribute(SERVICE_STATE) == Lifecycle.STARTING) {
+                    if (getAttribute(SERVICE_STATE_ACTUAL) == 
Lifecycle.STARTING) {
                         if (Sets.intersection(currentSeeds, 
potentialSeeds).isEmpty()) {
                             log.warn("Cluster {} lost all its seeds while 
starting! Subsequent failure likely, but changing seeds during startup would 
risk split-brain: seeds={}", new Object[] {CassandraDatacenterImpl.this, 
currentSeeds});
                         }
@@ -426,12 +427,6 @@ public class CassandraDatacenterImpl extends 
DynamicClusterImpl implements Cassa
                     .build());
 
         }
-
-        subscribeToMembers(this, SERVICE_UP, new 
SensorEventListener<Boolean>() {
-            @Override public void onEvent(SensorEvent<Boolean> event) {
-                setAttribute(SERVICE_UP, calculateServiceUp());
-            }
-        });
     }
 
     @Override
@@ -477,19 +472,11 @@ public class CassandraDatacenterImpl extends 
DynamicClusterImpl implements Cassa
                 setAttribute(THRIFT_PORT, null);
                 setAttribute(CASSANDRA_CLUSTER_NODES, 
Collections.<String>emptyList());
             }
-            
-            setAttribute(SERVICE_UP, upNode.isPresent() && 
calculateServiceUp());
+
+            ServiceNotUpLogic.updateNotUpIndicatorRequiringNonEmptyList(this, 
CASSANDRA_CLUSTER_NODES);
         }
     }
     
-    @Override
-    protected boolean calculateServiceUp() {
-        if (!super.calculateServiceUp()) return false;
-        List<String> nodes = getAttribute(CASSANDRA_CLUSTER_NODES);
-        if (nodes==null || nodes.isEmpty()) return false;
-        return true;
-    }
-    
     /**
      * For tracking our seeds. This gets fiddly! High-level logic is:
      * <ul>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/nosql/src/main/java/brooklyn/entity/nosql/cassandra/CassandraFabricImpl.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/main/java/brooklyn/entity/nosql/cassandra/CassandraFabricImpl.java
 
b/software/nosql/src/main/java/brooklyn/entity/nosql/cassandra/CassandraFabricImpl.java
index 5d3c219..fe0b503 100644
--- 
a/software/nosql/src/main/java/brooklyn/entity/nosql/cassandra/CassandraFabricImpl.java
+++ 
b/software/nosql/src/main/java/brooklyn/entity/nosql/cassandra/CassandraFabricImpl.java
@@ -72,8 +72,6 @@ public class CassandraFabricImpl extends DynamicFabricImpl 
implements CassandraF
     // Mutex for synchronizing during re-size operations
     private final Object mutex = new Object[0];
 
-    private MemberTrackingPolicy policy;
-
     private final Supplier<Set<Entity>> defaultSeedSupplier = new 
Supplier<Set<Entity>>() {
         @Override public Set<Entity> get() {
             // TODO Remove duplication from 
CassandraClusterImpl.defaultSeedSupplier
@@ -95,7 +93,7 @@ public class CassandraFabricImpl extends DynamicFabricImpl 
implements CassandraF
                 
                 if (hasPublishedSeeds) {
                     Set<Entity> currentSeeds = getAttribute(CURRENT_SEEDS);
-                    Lifecycle serviceState = getAttribute(SERVICE_STATE);
+                    Lifecycle serviceState = 
getAttribute(SERVICE_STATE_ACTUAL);
                     if (serviceState == Lifecycle.STARTING) {
                         if (Sets.intersection(currentSeeds, 
ImmutableSet.copyOf(Iterables.concat(potentialSeeds.values()))).isEmpty()) {
                             log.warn("Fabric {} lost all its seeds while 
starting! Subsequent failure likely, but changing seeds during startup would 
risk split-brain: seeds={}", new Object[] {CassandraFabricImpl.this, 
currentSeeds});
@@ -211,7 +209,7 @@ public class CassandraFabricImpl extends DynamicFabricImpl 
implements CassandraF
             setConfig(CassandraDatacenter.SEED_SUPPLIER, getSeedSupplier());
         
         // track members
-        policy = addPolicy(PolicySpec.create(MemberTrackingPolicy.class)
+        addPolicy(PolicySpec.create(MemberTrackingPolicy.class)
                 .displayName("Cassandra Fabric Tracker")
                 .configure("group", this));
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseClusterImpl.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseClusterImpl.java
 
b/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseClusterImpl.java
index 11c56d7..b47f133 100644
--- 
a/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseClusterImpl.java
+++ 
b/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseClusterImpl.java
@@ -35,13 +35,13 @@ import brooklyn.entity.basic.Attributes;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.basic.Lifecycle;
+import brooklyn.entity.basic.QuorumCheck;
+import brooklyn.entity.basic.ServiceStateLogic;
 import brooklyn.entity.group.AbstractMembershipTrackingPolicy;
 import brooklyn.entity.group.DynamicClusterImpl;
 import brooklyn.entity.proxying.EntitySpec;
 import brooklyn.entity.trait.Startable;
 import brooklyn.event.AttributeSensor;
-import brooklyn.event.SensorEvent;
-import brooklyn.event.SensorEventListener;
 import brooklyn.location.Location;
 import brooklyn.policy.PolicySpec;
 import brooklyn.util.collections.MutableSet;
@@ -129,8 +129,7 @@ public class CouchbaseClusterImpl extends 
DynamicClusterImpl implements Couchbas
         super.start(locations);
 
         connectSensors();
-        connectEnrichers();
-
+        
         //start timeout before adding the servers
         Time.sleep(getConfig(SERVICE_UP_TIME_OUT));
 
@@ -165,7 +164,7 @@ public class CouchbaseClusterImpl extends 
DynamicClusterImpl implements Couchbas
                 //check Repeater.
             }
         } else {
-            setAttribute(SERVICE_STATE, Lifecycle.ON_FIRE);
+            ServiceStateLogic.setExpectedState(this, Lifecycle.ON_FIRE);
         }
 
     }
@@ -175,16 +174,6 @@ public class CouchbaseClusterImpl extends 
DynamicClusterImpl implements Couchbas
         super.stop();
     }
 
-    public void connectEnrichers() {
-
-        subscribeToMembers(this, SERVICE_UP, new 
SensorEventListener<Boolean>() {
-            @Override
-            public void onEvent(SensorEvent<Boolean> event) {
-                setAttribute(SERVICE_UP, calculateServiceUp());
-            }
-        });
-    }
-
     protected void connectSensors() {
         addPolicy(PolicySpec.create(MemberTrackingPolicy.class)
                 .displayName("Controller targets tracker")
@@ -292,13 +281,22 @@ public class CouchbaseClusterImpl extends 
DynamicClusterImpl implements Couchbas
     }
 
     @Override
-    protected boolean calculateServiceUp() {
-        if (!super.calculateServiceUp()) return false;
-        Set<Entity> upNodes = getAttribute(COUCHBASE_CLUSTER_UP_NODES);
-        if (upNodes == null || upNodes.isEmpty() || upNodes.size() < 
getQuorumSize()) return false;
-        return true;
+    protected void initEnrichers() {
+        if (getConfigRaw(UP_QUORUM_CHECK, false).isAbsent()) {
+            class CouchbaseQuorumCheck implements QuorumCheck {
+                @Override
+                public boolean isQuorate(int sizeHealthy, int totalSize) {
+                    // check members count passed in AND the sensor  
+                    if (sizeHealthy < getQuorumSize()) return false;
+                    Set<Entity> upNodes = 
getAttribute(COUCHBASE_CLUSTER_UP_NODES);
+                    return (upNodes != null && !upNodes.isEmpty() && 
upNodes.size() >= getQuorumSize());
+                }
+            }
+            setConfig(UP_QUORUM_CHECK, new CouchbaseQuorumCheck());
+        }
+        super.initEnrichers();
     }
-
+    
     protected void addServers(Set<Entity> serversToAdd) {
         Preconditions.checkNotNull(serversToAdd);
         for (Entity e : serversToAdd) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/nosql/src/main/java/brooklyn/entity/nosql/couchdb/CouchDBClusterImpl.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/main/java/brooklyn/entity/nosql/couchdb/CouchDBClusterImpl.java
 
b/software/nosql/src/main/java/brooklyn/entity/nosql/couchdb/CouchDBClusterImpl.java
index a149140..4835f72 100644
--- 
a/software/nosql/src/main/java/brooklyn/entity/nosql/couchdb/CouchDBClusterImpl.java
+++ 
b/software/nosql/src/main/java/brooklyn/entity/nosql/couchdb/CouchDBClusterImpl.java
@@ -18,22 +18,18 @@
  */
 package brooklyn.entity.nosql.couchdb;
 
-import java.util.Collection;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.entity.Entity;
 import brooklyn.entity.group.DynamicClusterImpl;
 import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.entity.trait.Startable;
-import brooklyn.location.Location;
 
 /**
  * Implementation of {@link CouchDBCluster}.
  */
 public class CouchDBClusterImpl extends DynamicClusterImpl implements 
CouchDBCluster {
 
+    @SuppressWarnings("unused")
     private static final Logger log = 
LoggerFactory.getLogger(CouchDBClusterImpl.class);
 
     public CouchDBClusterImpl() {
@@ -52,19 +48,4 @@ public class CouchDBClusterImpl extends DynamicClusterImpl 
implements CouchDBClu
         return getAttribute(CLUSTER_NAME);
     }
 
-    @Override
-    public void start(Collection<? extends Location> locations) {
-        super.start(locations);
-
-        setAttribute(Startable.SERVICE_UP, calculateServiceUp());
-    }
-
-    @Override
-    protected boolean calculateServiceUp() {
-        boolean up = false;
-        for (Entity member : getMembers()) {
-            if (Boolean.TRUE.equals(member.getAttribute(SERVICE_UP))) up = 
true;
-        }
-        return up;
-    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/nosql/src/main/java/brooklyn/entity/nosql/elasticsearch/ElasticSearchClusterImpl.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/main/java/brooklyn/entity/nosql/elasticsearch/ElasticSearchClusterImpl.java
 
b/software/nosql/src/main/java/brooklyn/entity/nosql/elasticsearch/ElasticSearchClusterImpl.java
index da719dd..a773006 100644
--- 
a/software/nosql/src/main/java/brooklyn/entity/nosql/elasticsearch/ElasticSearchClusterImpl.java
+++ 
b/software/nosql/src/main/java/brooklyn/entity/nosql/elasticsearch/ElasticSearchClusterImpl.java
@@ -20,7 +20,6 @@ package brooklyn.entity.nosql.elasticsearch;
 
 import java.util.concurrent.atomic.AtomicInteger;
 
-import brooklyn.entity.Entity;
 import brooklyn.entity.group.DynamicClusterImpl;
 import brooklyn.entity.proxying.EntitySpec;
 
@@ -29,15 +28,6 @@ public class ElasticSearchClusterImpl extends 
DynamicClusterImpl implements Elas
     private AtomicInteger nextMemberId = new AtomicInteger(0);
 
     @Override
-    protected boolean calculateServiceUp() {
-        boolean up = false;
-        for (Entity member : getMembers()) {
-            if (Boolean.TRUE.equals(member.getAttribute(SERVICE_UP))) up = 
true;
-        }
-        return up;
-    }
-    
-    @Override
     protected EntitySpec<?> getMemberSpec() {
         EntitySpec<?> spec = EntitySpec.create(getConfig(MEMBER_SPEC, 
EntitySpec.create(ElasticSearchNode.class)));
         

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/nosql/src/main/java/brooklyn/entity/nosql/mongodb/MongoDBReplicaSetImpl.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/main/java/brooklyn/entity/nosql/mongodb/MongoDBReplicaSetImpl.java
 
b/software/nosql/src/main/java/brooklyn/entity/nosql/mongodb/MongoDBReplicaSetImpl.java
index b859765..b1e2393 100644
--- 
a/software/nosql/src/main/java/brooklyn/entity/nosql/mongodb/MongoDBReplicaSetImpl.java
+++ 
b/software/nosql/src/main/java/brooklyn/entity/nosql/mongodb/MongoDBReplicaSetImpl.java
@@ -40,6 +40,7 @@ import org.slf4j.LoggerFactory;
 import brooklyn.enricher.Enrichers;
 import brooklyn.entity.Entity;
 import brooklyn.entity.basic.Lifecycle;
+import brooklyn.entity.basic.ServiceStateLogic;
 import brooklyn.entity.group.AbstractMembershipTrackingPolicy;
 import brooklyn.entity.group.DynamicClusterImpl;
 import brooklyn.entity.proxying.EntitySpec;
@@ -200,7 +201,7 @@ public class MongoDBReplicaSetImpl extends 
DynamicClusterImpl implements MongoDB
                 setAttribute(PRIMARY_ENTITY, server);
                 setAttribute(Startable.SERVICE_UP, true);
             } else {
-                setAttribute(SERVICE_STATE, Lifecycle.ON_FIRE);
+                ServiceStateLogic.setExpectedState(this, Lifecycle.ON_FIRE);
             }
         } else {
             if (LOG.isDebugEnabled())

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/nosql/src/main/java/brooklyn/entity/nosql/mongodb/sharding/MongoDBConfigServerClusterImpl.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/main/java/brooklyn/entity/nosql/mongodb/sharding/MongoDBConfigServerClusterImpl.java
 
b/software/nosql/src/main/java/brooklyn/entity/nosql/mongodb/sharding/MongoDBConfigServerClusterImpl.java
index 28ee33a..df85c36 100644
--- 
a/software/nosql/src/main/java/brooklyn/entity/nosql/mongodb/sharding/MongoDBConfigServerClusterImpl.java
+++ 
b/software/nosql/src/main/java/brooklyn/entity/nosql/mongodb/sharding/MongoDBConfigServerClusterImpl.java
@@ -38,23 +38,11 @@ public class MongoDBConfigServerClusterImpl extends 
DynamicClusterImpl implement
         return EntitySpec.create(MongoDBConfigServer.class);
     }
     
-
-    @Override
-    protected boolean calculateServiceUp() {
-        // Number of config servers is fixed at INITIAL_SIZE
-        int requiredMembers = this.getConfig(INITIAL_SIZE);
-        int availableMembers = 0;
-        for (Entity entity : getMembers()) {
-            if (entity instanceof MongoDBConfigServer & 
entity.getAttribute(SERVICE_UP)) {
-                availableMembers++;
-            }
-        }
-        return availableMembers >= requiredMembers;
-    }
-    
     @Override
     public void start(Collection<? extends Location> locs) {
         super.start(locs);
+        
+        // TODO this should be an enricher
         Iterable<String> memberHostNamesAndPorts = 
Iterables.transform(getMembers(), new Function<Entity, String>() {
             @Override
             public String apply(Entity entity) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/nosql/src/main/java/brooklyn/entity/nosql/riak/RiakClusterImpl.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/main/java/brooklyn/entity/nosql/riak/RiakClusterImpl.java 
b/software/nosql/src/main/java/brooklyn/entity/nosql/riak/RiakClusterImpl.java
index 9826300..a75c422 100644
--- 
a/software/nosql/src/main/java/brooklyn/entity/nosql/riak/RiakClusterImpl.java
+++ 
b/software/nosql/src/main/java/brooklyn/entity/nosql/riak/RiakClusterImpl.java
@@ -21,6 +21,7 @@ package brooklyn.entity.nosql.riak;
 import static brooklyn.util.JavaGroovyEquivalents.groovyTruth;
 
 import java.util.Collection;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -39,6 +40,8 @@ import brooklyn.entity.Entity;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.basic.Lifecycle;
+import brooklyn.entity.basic.ServiceStateLogic;
+import brooklyn.entity.basic.ServiceStateLogic.ServiceNotUpLogic;
 import brooklyn.entity.group.AbstractMembershipTrackingPolicy;
 import brooklyn.entity.group.DynamicClusterImpl;
 import brooklyn.entity.proxying.EntitySpec;
@@ -80,7 +83,7 @@ public class RiakClusterImpl extends DynamicClusterImpl 
implements RiakCluster {
             setAttribute(IS_CLUSTER_INIT, true);
         } else {
             log.warn("No Riak Nodes are found on the cluster: {}. 
Initialization Failed", getId());
-            setAttribute(SERVICE_STATE, Lifecycle.ON_FIRE);
+            ServiceStateLogic.setExpectedState(this, Lifecycle.ON_FIRE);
         }
     }
 
@@ -100,11 +103,11 @@ public class RiakClusterImpl extends DynamicClusterImpl 
implements RiakCluster {
         if (log.isTraceEnabled()) log.trace("For {}, considering membership of 
{} which is in locations {}",
                 new Object[]{this, member, member.getLocations()});
 
+        Map<Entity, String> nodes = getAttribute(RIAK_CLUSTER_NODES);
         if (belongsInServerPool(member)) {
             // TODO can we discover the nodes by asking the riak cluster, 
rather than assuming what we add will be in there?
             // TODO and can we do join as part of node starting?
 
-            Map<Entity, String> nodes = getAttribute(RIAK_CLUSTER_NODES);
             if (nodes == null) nodes = Maps.newLinkedHashMap();
             String riakName = getRiakName(member);
 
@@ -151,7 +154,6 @@ public class RiakClusterImpl extends DynamicClusterImpl 
implements RiakCluster {
                 }
             }
         } else {
-            Map<Entity, String> nodes = getAttribute(RIAK_CLUSTER_NODES);
             if (nodes != null && nodes.containsKey(member)) {
                 final Entity memberToBeRemoved = member;
 
@@ -172,6 +174,8 @@ public class RiakClusterImpl extends DynamicClusterImpl 
implements RiakCluster {
 
             }
         }
+        
+        ServiceNotUpLogic.updateNotUpIndicatorRequiringNonEmptyMap(this, 
RIAK_CLUSTER_NODES);
         if (log.isTraceEnabled()) log.trace("Done {} checkEntity {}", this, 
member);
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/webapp/src/main/java/brooklyn/entity/proxy/AbstractControllerImpl.java
----------------------------------------------------------------------
diff --git 
a/software/webapp/src/main/java/brooklyn/entity/proxy/AbstractControllerImpl.java
 
b/software/webapp/src/main/java/brooklyn/entity/proxy/AbstractControllerImpl.java
index 9e0b151..d405fab 100644
--- 
a/software/webapp/src/main/java/brooklyn/entity/proxy/AbstractControllerImpl.java
+++ 
b/software/webapp/src/main/java/brooklyn/entity/proxy/AbstractControllerImpl.java
@@ -325,7 +325,7 @@ public abstract class AbstractControllerImpl extends 
SoftwareProcessImpl impleme
     @Override
     protected void postRebind() {
         super.postRebind();
-        Lifecycle state = getAttribute(SERVICE_STATE);
+        Lifecycle state = getAttribute(SERVICE_STATE_ACTUAL);
         if (state != null && state == Lifecycle.RUNNING) {
             isActive = true;
             update();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/webapp/src/main/java/brooklyn/entity/proxy/LoadBalancerClusterImpl.java
----------------------------------------------------------------------
diff --git 
a/software/webapp/src/main/java/brooklyn/entity/proxy/LoadBalancerClusterImpl.java
 
b/software/webapp/src/main/java/brooklyn/entity/proxy/LoadBalancerClusterImpl.java
index 8c4f389..bc8f88d 100644
--- 
a/software/webapp/src/main/java/brooklyn/entity/proxy/LoadBalancerClusterImpl.java
+++ 
b/software/webapp/src/main/java/brooklyn/entity/proxy/LoadBalancerClusterImpl.java
@@ -18,14 +18,10 @@
  */
 package brooklyn.entity.proxy;
 
-import java.util.Collection;
 import java.util.Map;
 
 import brooklyn.entity.Entity;
-import brooklyn.entity.group.AbstractMembershipTrackingPolicy;
 import brooklyn.entity.group.DynamicClusterImpl;
-import brooklyn.location.Location;
-import brooklyn.policy.PolicySpec;
 
 /**
  * A cluster of load balancers, where configuring the cluster (through the 
LoadBalancer interface)
@@ -49,33 +45,6 @@ public class LoadBalancerClusterImpl extends 
DynamicClusterImpl implements LoadB
         super();
     }
 
-    @Override
-    public void start(Collection<? extends Location> locs) {
-        super.start(locs);
-        
-        // TODO Is there a race here, where (dispite super.stop() calling 
policy.suspend),
-        // this could still be executing setAttribute(true) and hence 
incorrectly leave
-        // the cluster in a service_up==true state after stop() returns?
-        addPolicy(PolicySpec.create(MemberTrackingPolicy.class)
-                .configure("group", this));
-    }
-
-    public static class MemberTrackingPolicy extends 
AbstractMembershipTrackingPolicy {
-        @Override protected void onEntityEvent(EventType type, Entity member) {
-            entity.setAttribute(SERVICE_UP, 
((LoadBalancerClusterImpl)entity).calculateServiceUp());
-        }
-    }
-
-    /**
-     * Up if running and has at least one load-balancer in the cluster.
-     * 
-     * TODO Could also look at service_up of each load-balancer, but currently 
does not do that.
-     */
-    @Override
-    protected boolean calculateServiceUp() {
-        return super.calculateServiceUp() && getCurrentSize() > 0;
-    }
-
     /* NOTE The following methods come from {@link LoadBalancer} but are 
probably safe to ignore */
     
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/webapp/src/main/java/brooklyn/entity/proxy/nginx/NginxControllerImpl.java
----------------------------------------------------------------------
diff --git 
a/software/webapp/src/main/java/brooklyn/entity/proxy/nginx/NginxControllerImpl.java
 
b/software/webapp/src/main/java/brooklyn/entity/proxy/nginx/NginxControllerImpl.java
index f482422..3e36ada 100644
--- 
a/software/webapp/src/main/java/brooklyn/entity/proxy/nginx/NginxControllerImpl.java
+++ 
b/software/webapp/src/main/java/brooklyn/entity/proxy/nginx/NginxControllerImpl.java
@@ -66,7 +66,7 @@ public class NginxControllerImpl extends 
AbstractControllerImpl implements Nginx
     public void reload() {
         NginxSshDriver driver = (NginxSshDriver)getDriver();
         if (driver==null) {
-            Lifecycle state = getAttribute(NginxController.SERVICE_STATE);
+            Lifecycle state = 
getAttribute(NginxController.SERVICE_STATE_ACTUAL);
             throw new IllegalStateException("Cannot reload (no driver 
instance; stopped? (state="+state+")");
         }
 
@@ -181,7 +181,7 @@ public class NginxControllerImpl extends 
AbstractControllerImpl implements Nginx
         if (driver==null) {
             if (LOG.isDebugEnabled())
                 LOG.debug("No driver for {}, so not deploying archive (is 
entity stopping? state={})",
-                        this, getAttribute(NginxController.SERVICE_STATE));
+                        this, 
getAttribute(NginxController.SERVICE_STATE_ACTUAL));
             return;
         }
 
@@ -248,7 +248,7 @@ public class NginxControllerImpl extends 
AbstractControllerImpl implements Nginx
         if (driver==null) {
             if (LOG.isDebugEnabled())
                 LOG.debug("No driver for {}, so not generating config file (is 
entity stopping? state={})",
-                        this, getAttribute(NginxController.SERVICE_STATE));
+                        this, 
getAttribute(NginxController.SERVICE_STATE_ACTUAL));
             return null;
         }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/webapp/src/main/java/brooklyn/entity/proxy/nginx/NginxSshDriver.java
----------------------------------------------------------------------
diff --git 
a/software/webapp/src/main/java/brooklyn/entity/proxy/nginx/NginxSshDriver.java 
b/software/webapp/src/main/java/brooklyn/entity/proxy/nginx/NginxSshDriver.java
index 81e74fd..8a3f2c6 100644
--- 
a/software/webapp/src/main/java/brooklyn/entity/proxy/nginx/NginxSshDriver.java
+++ 
b/software/webapp/src/main/java/brooklyn/entity/proxy/nginx/NginxSshDriver.java
@@ -380,8 +380,8 @@ public class NginxSshDriver extends 
AbstractSoftwareProcessSshDriver implements
         // calling waitForEntityStart()), we can guarantee that the 
start-thread's call to update will happen after
         // this call to reload. So we this can be a no-op, and just rely on 
that subsequent call to update.
 
-        Lifecycle lifecycle = 
entity.getAttribute(NginxController.SERVICE_STATE);
         if (!isRunning()) {
+            Lifecycle lifecycle = 
entity.getAttribute(NginxController.SERVICE_STATE_ACTUAL);
             log.debug("Ignoring reload of nginx "+entity+", because service is 
not running (state "+lifecycle+")");
             return;
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/webapp/src/main/java/brooklyn/entity/webapp/ControlledDynamicWebAppCluster.java
----------------------------------------------------------------------
diff --git 
a/software/webapp/src/main/java/brooklyn/entity/webapp/ControlledDynamicWebAppCluster.java
 
b/software/webapp/src/main/java/brooklyn/entity/webapp/ControlledDynamicWebAppCluster.java
index 0c263d9..72b2bb6 100644
--- 
a/software/webapp/src/main/java/brooklyn/entity/webapp/ControlledDynamicWebAppCluster.java
+++ 
b/software/webapp/src/main/java/brooklyn/entity/webapp/ControlledDynamicWebAppCluster.java
@@ -93,7 +93,7 @@ public interface ControlledDynamicWebAppCluster extends 
DynamicGroup, Entity, St
 
     public static final AttributeSensor<String> HOSTNAME = Attributes.HOSTNAME;
 
-    public static final AttributeSensor<Lifecycle> SERVICE_STATE = 
Attributes.SERVICE_STATE;
+    public static final AttributeSensor<Lifecycle> SERVICE_STATE_ACTUAL = 
Attributes.SERVICE_STATE_ACTUAL;
 
     
     public LoadBalancer getController();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/webapp/src/main/java/brooklyn/entity/webapp/ControlledDynamicWebAppClusterImpl.java
----------------------------------------------------------------------
diff --git 
a/software/webapp/src/main/java/brooklyn/entity/webapp/ControlledDynamicWebAppClusterImpl.java
 
b/software/webapp/src/main/java/brooklyn/entity/webapp/ControlledDynamicWebAppClusterImpl.java
index 47e7ec9..acff382 100644
--- 
a/software/webapp/src/main/java/brooklyn/entity/webapp/ControlledDynamicWebAppClusterImpl.java
+++ 
b/software/webapp/src/main/java/brooklyn/entity/webapp/ControlledDynamicWebAppClusterImpl.java
@@ -19,10 +19,9 @@
 package brooklyn.entity.webapp;
 
 import java.util.Collection;
-import java.util.EnumSet;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.ExecutionException;
+import java.util.Set;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -35,6 +34,9 @@ import brooklyn.entity.basic.DynamicGroupImpl;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityPredicates;
 import brooklyn.entity.basic.Lifecycle;
+import brooklyn.entity.basic.QuorumCheck;
+import brooklyn.entity.basic.QuorumCheck.QuorumChecks;
+import brooklyn.entity.basic.ServiceStateLogic;
 import brooklyn.entity.proxy.LoadBalancer;
 import brooklyn.entity.proxy.nginx.NginxController;
 import brooklyn.entity.proxying.EntitySpec;
@@ -71,7 +73,6 @@ public class ControlledDynamicWebAppClusterImpl extends 
DynamicGroupImpl impleme
     @Deprecated
     public ControlledDynamicWebAppClusterImpl(Map<?,?> flags, Entity parent) {
         super(flags, parent);
-        setAttribute(SERVICE_UP, false);
     }
 
     @Override
@@ -137,6 +138,14 @@ public class ControlledDynamicWebAppClusterImpl extends 
DynamicGroupImpl impleme
         
         doBind();
     }
+
+    @Override
+    protected void initEnrichers() {
+        if (getConfigRaw(UP_QUORUM_CHECK, false).isAbsent()) {
+            setConfig(UP_QUORUM_CHECK, QuorumChecks.newInstance(2, 1.0, 
false));
+        }
+        super.initEnrichers();
+    }
     
     @Override
     public void rebind() {
@@ -174,7 +183,7 @@ public class ControlledDynamicWebAppClusterImpl extends 
DynamicGroupImpl impleme
     
     @Override
     public void start(Collection<? extends Location> locations) {
-        setAttribute(Attributes.SERVICE_STATE, Lifecycle.STARTING);
+        ServiceStateLogic.setExpectedState(this, Lifecycle.STARTING);
 
         try {
             if (isLegacyConstruction()) {
@@ -202,13 +211,9 @@ public class ControlledDynamicWebAppClusterImpl extends 
DynamicGroupImpl impleme
             // (will happen asynchronously as members come online, but we want 
to force it to happen)
             getController().update();
 
-            setAttribute(SERVICE_UP, getCluster().getAttribute(SERVICE_UP));
-            setAttribute(SERVICE_STATE, Lifecycle.RUNNING);
-        } catch (InterruptedException e) {
-            setAttribute(Attributes.SERVICE_STATE, Lifecycle.ON_FIRE);
-            throw Exceptions.propagate(e);
-        } catch (ExecutionException e) {
-            setAttribute(Attributes.SERVICE_STATE, Lifecycle.ON_FIRE);
+            ServiceStateLogic.setExpectedState(this, Lifecycle.RUNNING);
+        } catch (Exception e) {
+            ServiceStateLogic.setExpectedState(this, Lifecycle.ON_FIRE);
             throw Exceptions.propagate(e);
         } finally {
             connectSensors();
@@ -217,7 +222,7 @@ public class ControlledDynamicWebAppClusterImpl extends 
DynamicGroupImpl impleme
 
     @Override
     public void stop() {
-        setAttribute(SERVICE_STATE, Lifecycle.STOPPING);
+        ServiceStateLogic.setExpectedState(this, Lifecycle.STOPPING);
 
         try {
             List<Startable> tostop = Lists.newArrayList();
@@ -228,10 +233,9 @@ public class ControlledDynamicWebAppClusterImpl extends 
DynamicGroupImpl impleme
 
             clearLocations();
 
-            setAttribute(SERVICE_STATE, Lifecycle.STOPPED);
-            setAttribute(SERVICE_UP, false);
+            ServiceStateLogic.setExpectedState(this, Lifecycle.STOPPED);
         } catch (Exception e) {
-            setAttribute(SERVICE_STATE, Lifecycle.ON_FIRE);
+            ServiceStateLogic.setExpectedState(this, Lifecycle.ON_FIRE);
             throw Exceptions.propagate(e);
         }
     }
@@ -246,8 +250,9 @@ public class ControlledDynamicWebAppClusterImpl extends 
DynamicGroupImpl impleme
     }
     
     void connectSensors() {
+        // FIXME no longer needed
         addEnricher(Enrichers.builder()
-                .propagatingAllBut(SERVICE_STATE, SERVICE_UP, ROOT_URL, 
GROUP_MEMBERS, GROUP_SIZE)
+                .propagatingAllButUsualAnd(ROOT_URL, GROUP_MEMBERS, GROUP_SIZE)
                 .from(getCluster())
                 .build());
         addEnricher(Enrichers.builder()
@@ -255,40 +260,6 @@ public class ControlledDynamicWebAppClusterImpl extends 
DynamicGroupImpl impleme
                 .propagating(LoadBalancer.HOSTNAME, Attributes.ADDRESS, 
ROOT_URL)
                 .from(getController())
                 .build());
-
-        SensorEventListener<Boolean> updateServiceUp = new 
SensorEventListener<Boolean>() {
-            @Override
-            public void onEvent(SensorEvent<Boolean> event) {
-                setAttribute(SERVICE_UP, calculateServiceUp());
-            }
-        };
-        SensorEventListener<Lifecycle> updateServiceState = new 
SensorEventListener<Lifecycle>() {
-            @Override
-            public void onEvent(SensorEvent<Lifecycle> event) {
-                setAttribute(SERVICE_STATE, calculateServiceState());
-            }
-        };
-        
-        subscribe(getCluster(), SERVICE_STATE, updateServiceState);
-        subscribe(getController(), SERVICE_STATE, updateServiceState);
-        subscribe(getCluster(), SERVICE_UP, updateServiceUp);
-        subscribe(getController(), SERVICE_UP, updateServiceUp);
-    }
-
-    protected Lifecycle calculateServiceState() {
-        Lifecycle currentState = getAttribute(SERVICE_STATE);
-        if (EnumSet.of(Lifecycle.ON_FIRE, 
Lifecycle.RUNNING).contains(currentState)) {
-            if (getCluster().getAttribute(SERVICE_STATE) == Lifecycle.ON_FIRE) 
currentState = Lifecycle.ON_FIRE;
-            if (getController().getAttribute(SERVICE_STATE) == 
Lifecycle.ON_FIRE) currentState = Lifecycle.ON_FIRE;
-        }
-        return currentState;
-    }
-
-    /**
-     * Default impl is to be up when running, and !up otherwise.
-     */
-    protected boolean calculateServiceUp() {
-        return getAttribute(SERVICE_STATE) == Lifecycle.RUNNING;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/webapp/src/main/java/brooklyn/entity/webapp/DynamicWebAppClusterImpl.java
----------------------------------------------------------------------
diff --git 
a/software/webapp/src/main/java/brooklyn/entity/webapp/DynamicWebAppClusterImpl.java
 
b/software/webapp/src/main/java/brooklyn/entity/webapp/DynamicWebAppClusterImpl.java
index bfdc666..951e3b4 100644
--- 
a/software/webapp/src/main/java/brooklyn/entity/webapp/DynamicWebAppClusterImpl.java
+++ 
b/software/webapp/src/main/java/brooklyn/entity/webapp/DynamicWebAppClusterImpl.java
@@ -37,8 +37,6 @@ import brooklyn.entity.effector.Effectors;
 import brooklyn.entity.group.DynamicCluster;
 import brooklyn.entity.group.DynamicClusterImpl;
 import brooklyn.event.AttributeSensor;
-import brooklyn.event.SensorEvent;
-import brooklyn.event.SensorEventListener;
 import brooklyn.management.Task;
 import brooklyn.management.TaskAdaptable;
 import brooklyn.util.collections.MutableMap;
@@ -123,45 +121,6 @@ public class DynamicWebAppClusterImpl extends 
DynamicClusterImpl implements Dyna
         }
     }
     
-    public void onManagementStarted() {
-        super.onManagementStarted();
-        
-        subscribeToMembers(this, SERVICE_UP, new 
SensorEventListener<Boolean>() {
-            @Override public void onEvent(SensorEvent<Boolean> event) {
-                if (!isRebinding()) {
-                    setAttribute(SERVICE_UP, calculateServiceUp());
-                }
-            }
-        });
-    }
-
-    @Override
-    public synchronized boolean addMember(Entity member) {
-        boolean result = super.addMember(member);
-        if (!isRebinding()) {
-            setAttribute(SERVICE_UP, calculateServiceUp());
-        }
-        return result;
-    }
-    
-    @Override
-    public synchronized boolean removeMember(Entity member) {
-        boolean result = super.removeMember(member);
-        if (!isRebinding()) {
-            setAttribute(SERVICE_UP, calculateServiceUp());
-        }
-        return result;
-    }
-
-    @Override    
-    protected boolean calculateServiceUp() {
-        boolean up = false;
-        for (Entity member : getMembers()) {
-            if (Boolean.TRUE.equals(member.getAttribute(SERVICE_UP))) up = 
true;
-        }
-        return up;
-    }
-    
     // TODO this will probably be useful elsewhere ... but where to put it?
     // TODO add support for this in DependentConfiguration (see TODO there)
     /** Waits for the given target to report service up, then runs the given 
task

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/webapp/src/main/java/brooklyn/entity/webapp/jetty/Jetty6ServerImpl.java
----------------------------------------------------------------------
diff --git 
a/software/webapp/src/main/java/brooklyn/entity/webapp/jetty/Jetty6ServerImpl.java
 
b/software/webapp/src/main/java/brooklyn/entity/webapp/jetty/Jetty6ServerImpl.java
index ce8fdec..9d2c7ec 100644
--- 
a/software/webapp/src/main/java/brooklyn/entity/webapp/jetty/Jetty6ServerImpl.java
+++ 
b/software/webapp/src/main/java/brooklyn/entity/webapp/jetty/Jetty6ServerImpl.java
@@ -130,7 +130,7 @@ public class Jetty6ServerImpl extends 
JavaWebAppSoftwareProcessImpl implements J
     
     protected void restartIfRunning() {
         // TODO for now we simply restart jetty to achieve "hot deployment"; 
should use the config mechanisms
-        Lifecycle serviceState = getAttribute(SERVICE_STATE);
+        Lifecycle serviceState = getAttribute(SERVICE_STATE_ACTUAL);
         if (serviceState == Lifecycle.RUNNING)
             restart();
         // may need a restart also if deploy effector is done in parallel to 
starting

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/webapp/src/test/java/brooklyn/entity/proxy/nginx/NginxRebindIntegrationTest.java
----------------------------------------------------------------------
diff --git 
a/software/webapp/src/test/java/brooklyn/entity/proxy/nginx/NginxRebindIntegrationTest.java
 
b/software/webapp/src/test/java/brooklyn/entity/proxy/nginx/NginxRebindIntegrationTest.java
index 74e23b5..f731eca 100644
--- 
a/software/webapp/src/test/java/brooklyn/entity/proxy/nginx/NginxRebindIntegrationTest.java
+++ 
b/software/webapp/src/test/java/brooklyn/entity/proxy/nginx/NginxRebindIntegrationTest.java
@@ -50,6 +50,7 @@ import brooklyn.location.LocationSpec;
 import brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 import brooklyn.management.ManagementContext;
 import brooklyn.test.Asserts;
+import brooklyn.test.EntityTestUtils;
 import brooklyn.test.WebAppMonitor;
 
 import com.google.common.base.Predicates;
@@ -134,7 +135,7 @@ public class NginxRebindIntegrationTest extends 
RebindTestFixtureWithApp {
 
         assertEquals(newNginx.getConfigFile(), origConfigFile);
         
-        assertEquals(newNginx.getAttribute(NginxController.SERVICE_STATE), 
Lifecycle.RUNNING);
+        EntityTestUtils.assertAttributeEqualsEventually(newNginx, 
NginxController.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING);
         assertEquals(newNginx.getAttribute(NginxController.PROXY_HTTP_PORT), 
(Integer)nginxPort);
         assertEquals(newNginx.getAttribute(NginxController.ROOT_URL), rootUrl);
         assertEquals(newNginx.getAttribute(NginxController.PROXY_HTTP_PORT), 
origNginx.getAttribute(NginxController.PROXY_HTTP_PORT));

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/webapp/src/test/java/brooklyn/entity/webapp/ControlledDynamicWebAppClusterTest.java
----------------------------------------------------------------------
diff --git 
a/software/webapp/src/test/java/brooklyn/entity/webapp/ControlledDynamicWebAppClusterTest.java
 
b/software/webapp/src/test/java/brooklyn/entity/webapp/ControlledDynamicWebAppClusterTest.java
index d5d960b..df486aa 100644
--- 
a/software/webapp/src/test/java/brooklyn/entity/webapp/ControlledDynamicWebAppClusterTest.java
+++ 
b/software/webapp/src/test/java/brooklyn/entity/webapp/ControlledDynamicWebAppClusterTest.java
@@ -317,6 +317,6 @@ public class ControlledDynamicWebAppClusterTest {
         Entities.unmanage(controller);
         
         cluster.stop();
-        EntityTestUtils.assertAttributeEquals(cluster, 
ControlledDynamicWebAppCluster.SERVICE_STATE, Lifecycle.STOPPED);
+        EntityTestUtils.assertAttributeEquals(cluster, 
ControlledDynamicWebAppCluster.SERVICE_STATE_ACTUAL, Lifecycle.STOPPED);
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/usage/rest-server/src/main/java/brooklyn/rest/transform/ApplicationTransformer.java
----------------------------------------------------------------------
diff --git 
a/usage/rest-server/src/main/java/brooklyn/rest/transform/ApplicationTransformer.java
 
b/usage/rest-server/src/main/java/brooklyn/rest/transform/ApplicationTransformer.java
index 7067898..88e8da4 100644
--- 
a/usage/rest-server/src/main/java/brooklyn/rest/transform/ApplicationTransformer.java
+++ 
b/usage/rest-server/src/main/java/brooklyn/rest/transform/ApplicationTransformer.java
@@ -57,7 +57,7 @@ public class ApplicationTransformer {
 
     public static Status statusFromApplication(Application application) {
         if (application == null) return UNKNOWN;
-        Lifecycle state = application.getAttribute(Attributes.SERVICE_STATE);
+        Lifecycle state = 
application.getAttribute(Attributes.SERVICE_STATE_ACTUAL);
         if (state != null) return statusFromLifecycle(state);
         Boolean up = application.getAttribute(Startable.SERVICE_UP);
         if (up != null && up.booleanValue()) return RUNNING;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/utils/common/src/main/java/brooklyn/util/guava/Functionals.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/util/guava/Functionals.java 
b/utils/common/src/main/java/brooklyn/util/guava/Functionals.java
index a93c551..ef66ded 100644
--- a/utils/common/src/main/java/brooklyn/util/guava/Functionals.java
+++ b/utils/common/src/main/java/brooklyn/util/guava/Functionals.java
@@ -23,6 +23,7 @@ import 
brooklyn.util.guava.IfFunctions.IfFunctionBuilderApplyingFirst;
 import com.google.common.base.Function;
 import com.google.common.base.Functions;
 import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
 
 public class Functionals {
 
@@ -80,4 +81,34 @@ public class Functionals {
         }
     }
 
+    /** like guava {@link Functions#forSupplier(Supplier)} but parametrises 
the input generic type */
+    public static <I,O> Function<I,O> function(final Supplier<O> supplier) {
+        class SupplierAsFunction implements Function<I,O> {
+            @Override public O apply(I input) {
+                return supplier.get();
+            }
+        }
+        return new SupplierAsFunction();
+    }
+
+    public static <I> Function<I,Void> function(final Runnable runnable) {
+        class RunnableAsFunction implements Function<I,Void> {
+            @Override public Void apply(I input) {
+                runnable.run();
+                return null;
+            }
+        }
+        return new RunnableAsFunction();
+    }
+
+    public static Runnable runnable(final Supplier<?> supplier) {
+        class SupplierAsRunnable implements Runnable {
+            @Override
+            public void run() {
+                supplier.get();
+            }
+        }
+        return new SupplierAsRunnable();
+    }
+
 }

Reply via email to