You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2014/08/30 01:01:11 UTC
[11/26] add SERVICE_PROBLEMS and enrichers in ServiceStateLogic to
compute state from that. add enrichers to populate PROBLEMS and
NOT_UP_INDICATORS from children and members: this removes a lot of ad hoc
service-up computations; some quorum up logic e.g
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();
+ }
+
}