http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/core/src/main/java/brooklyn/entity/basic/ServiceStateLogic.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/entity/basic/ServiceStateLogic.java b/core/src/main/java/brooklyn/entity/basic/ServiceStateLogic.java new file mode 100644 index 0000000..de4f1ad --- /dev/null +++ b/core/src/main/java/brooklyn/entity/basic/ServiceStateLogic.java @@ -0,0 +1,440 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package brooklyn.entity.basic; + +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nullable; + +import brooklyn.config.ConfigKey; +import brooklyn.enricher.Enrichers; +import brooklyn.enricher.basic.AbstractEnricher; +import brooklyn.enricher.basic.AbstractMultipleSensorAggregator; +import brooklyn.enricher.basic.UpdatingMap; +import brooklyn.entity.Effector; +import brooklyn.entity.Entity; +import brooklyn.entity.Group; +import brooklyn.entity.basic.Lifecycle.Transition; +import brooklyn.event.AttributeSensor; +import brooklyn.event.Sensor; +import brooklyn.event.SensorEvent; +import brooklyn.event.SensorEventListener; +import brooklyn.policy.Enricher; +import brooklyn.policy.EnricherSpec; +import brooklyn.policy.EnricherSpec.ExtensibleEnricherSpec; +import brooklyn.util.collections.CollectionFunctionals; +import brooklyn.util.collections.MutableList; +import brooklyn.util.collections.MutableMap; +import brooklyn.util.guava.Functionals; +import brooklyn.util.text.Strings; + +import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; + +/** Logic, sensors and enrichers, and conveniences, for computing service status */ +public class ServiceStateLogic { + + public static final AttributeSensor<Boolean> SERVICE_UP = Attributes.SERVICE_UP; + public static final AttributeSensor<Map<String,Object>> SERVICE_NOT_UP_INDICATORS = Attributes.SERVICE_NOT_UP_INDICATORS; + + public static final AttributeSensor<Lifecycle> SERVICE_STATE_ACTUAL = Attributes.SERVICE_STATE_ACTUAL; + public static final AttributeSensor<Lifecycle.Transition> SERVICE_STATE_EXPECTED = Attributes.SERVICE_STATE_EXPECTED; + public static final AttributeSensor<Map<String,Object>> SERVICE_PROBLEMS = Attributes.SERVICE_PROBLEMS; + + /** static only; not for instantiation */ + private ServiceStateLogic() {} + + @SuppressWarnings("unchecked") + public static <TKey,TVal> void clearMapSensorEntry(EntityLocal entity, AttributeSensor<Map<TKey,TVal>> sensor, TKey key) { + updateMapSensorEntry(entity, sensor, key, (TVal)Entities.REMOVE); + } + + /** update the given key in the given map sensor */ + public static <TKey,TVal> void updateMapSensorEntry(EntityLocal entity, AttributeSensor<Map<TKey,TVal>> sensor, TKey key, TVal v) { + Map<TKey, TVal> map = entity.getAttribute(sensor); + + // TODO synchronize + + boolean created = (map==null); + if (created) map = MutableMap.of(); + + boolean changed; + if (v == Entities.REMOVE) { + changed = map.containsKey(key); + if (changed) + map.remove(key); + } else { + TVal oldV = map.get(key); + if (oldV==null) + changed = (v!=null || !map.containsKey(key)); + else + changed = !oldV.equals(v); + if (changed) + map.put(key, (TVal)v); + } + if (changed || created) + entity.setAttribute(sensor, map); + } + + public static void setExpectedState(Entity entity, Lifecycle state) { + ((EntityInternal)entity).setAttribute(Attributes.SERVICE_STATE_EXPECTED, new Lifecycle.Transition(state, new Date())); + } + public static Lifecycle getExpectedState(Entity entity) { + Transition expected = entity.getAttribute(Attributes.SERVICE_STATE_EXPECTED); + if (expected==null) return null; + return expected.getState(); + } + public static boolean isExpectedState(Entity entity, Lifecycle state) { + return getExpectedState(entity)==state; + } + + public static class ServiceNotUpLogic { + /** static only; not for instantiation */ + private ServiceNotUpLogic() {} + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public static final EnricherSpec<?> newEnricherForServiceUpIfNoNotUpIndicators() { + return Enrichers.builder() + .transforming(SERVICE_NOT_UP_INDICATORS).publishing(Attributes.SERVICE_UP) + .computing( /* cast hacks to support removing */ (Function) + Functionals.<Map<String,?>> + ifNotEquals(null).<Object>apply(Functions.forPredicate(CollectionFunctionals.<String>mapSizeEquals(0))) + .defaultValue(Entities.REMOVE) ) + .uniqueTag("service.isUp if no service.notUp.indicators") + .build(); + } + + /** puts the given value into the {@link Attributes#SERVICE_NOT_UP_INDICATORS} map as if the + * {@link UpdatingMap} enricher for the given sensor reported */ + public static void updateNotUpIndicator(EntityLocal entity, Sensor<?> sensor, Object value) { + updateMapSensorEntry(entity, Attributes.SERVICE_NOT_UP_INDICATORS, sensor.getName(), value); + } + /** clears any entry for the given sensor in the {@link Attributes#SERVICE_NOT_UP_INDICATORS} map */ + public static void clearNotUpIndicator(EntityLocal entity, Sensor<?> sensor) { + clearMapSensorEntry(entity, Attributes.SERVICE_NOT_UP_INDICATORS, sensor.getName()); + } + + public static void updateNotUpIndicatorRequiringNonEmptyList(EntityLocal entity, AttributeSensor<? extends Collection<?>> collectionSensor) { + Collection<?> nodes = entity.getAttribute(collectionSensor); + if (nodes==null || nodes.isEmpty()) ServiceNotUpLogic.updateNotUpIndicator(entity, collectionSensor, "Should have at least one entry"); + else ServiceNotUpLogic.clearNotUpIndicator(entity, collectionSensor); + } + public static void updateNotUpIndicatorRequiringNonEmptyMap(EntityLocal entity, AttributeSensor<? extends Map<?,?>> mapSensor) { + Map<?, ?> nodes = entity.getAttribute(mapSensor); + if (nodes==null || nodes.isEmpty()) ServiceNotUpLogic.updateNotUpIndicator(entity, mapSensor, "Should have at least one entry"); + else ServiceNotUpLogic.clearNotUpIndicator(entity, mapSensor); + } + + } + + /** Enricher which sets {@link Attributes#SERVICE_STATE_ACTUAL} on changes to + * {@link Attributes#SERVICE_STATE_EXPECTED}, {@link Attributes#SERVICE_PROBLEMS}, and {@link Attributes#SERVICE_UP} + * <p> + * The default implementation uses {@link #computeActualStateWhenExpectedRunning(Map, Boolean)} if the last expected transition + * was to {@link Lifecycle#RUNNING} and + * {@link #computeActualStateWhenNotExpectedRunning(Map, Boolean, brooklyn.entity.basic.Lifecycle.Transition)} otherwise. + * If these methods return null, the {@link Attributes#SERVICE_STATE_ACTUAL} sensor will be cleared (removed). + * Either of these methods can be overridden for custom logic, and that custom enricher can be created using + * {@link ServiceStateLogic#newEnricherForServiceState(Class)} and added to an entity. + */ + public static class ComputeServiceState extends AbstractEnricher implements SensorEventListener<Object> { + public void setEntity(EntityLocal entity) { + super.setEntity(entity); + if (suppressDuplicates==null) { + // only publish on changes, unless it is configured otherwise + suppressDuplicates = Boolean.TRUE; + } + + subscribe(entity, SERVICE_PROBLEMS, this); + subscribe(entity, SERVICE_UP, this); + subscribe(entity, SERVICE_STATE_EXPECTED, this); + onEvent(null); + } + + @Override + public void onEvent(@Nullable SensorEvent<Object> event) { + Preconditions.checkNotNull(entity, "Cannot handle subscriptions or compute state until associated with an entity"); + + Map<String, Object> serviceProblems = entity.getAttribute(SERVICE_PROBLEMS); + Boolean serviceUp = entity.getAttribute(SERVICE_UP); + Lifecycle.Transition serviceExpected = entity.getAttribute(SERVICE_STATE_EXPECTED); + + if (serviceExpected!=null && serviceExpected.getState()==Lifecycle.RUNNING) { + setActualState( computeActualStateWhenExpectedRunning(serviceProblems, serviceUp) ); + } else { + setActualState( computeActualStateWhenNotExpectedRunning(serviceProblems, serviceUp, serviceExpected) ); + } + } + + protected Lifecycle computeActualStateWhenExpectedRunning(Map<String, Object> problems, Boolean serviceUp) { + if (Boolean.TRUE.equals(serviceUp) && (problems==null || problems.isEmpty())) { + return Lifecycle.RUNNING; + } else { + return Lifecycle.ON_FIRE; + } + } + + protected Lifecycle computeActualStateWhenNotExpectedRunning(Map<String, Object> problems, Boolean up, Lifecycle.Transition stateTransition) { + if (stateTransition!=null) { + return stateTransition.getState(); + } else if (problems!=null && !problems.isEmpty()) { + return Lifecycle.ON_FIRE; + } else { + return (up==null ? null : up ? Lifecycle.RUNNING : Lifecycle.STOPPED); + } + } + + protected void setActualState(@Nullable Lifecycle state) { + emit(SERVICE_STATE_ACTUAL, (state==null ? Entities.REMOVE : state)); + } + } + + public static final EnricherSpec<?> newEnricherForServiceStateFromProblemsAndUp() { + return newEnricherForServiceState(ComputeServiceState.class); + } + public static final EnricherSpec<?> newEnricherForServiceState(Class<? extends Enricher> type) { + return EnricherSpec.create(type).uniqueTag("service.state.actual from service.state.expected and service.problems"); + } + + public static class ServiceProblemsLogic { + /** static only; not for instantiation */ + private ServiceProblemsLogic() {} + + /** puts the given value into the {@link Attributes#SERVICE_PROBLEMS} map as if the + * {@link UpdatingMap} enricher for the given sensor reported this value */ + public static void updateProblemsIndicator(EntityLocal entity, Sensor<?> sensor, Object value) { + updateMapSensorEntry(entity, Attributes.SERVICE_PROBLEMS, sensor.getName(), value); + } + /** clears any entry for the given sensor in the {@link Attributes#SERVICE_PROBLEMS} map */ + public static void clearProblemsIndicator(EntityLocal entity, Sensor<?> sensor) { + clearMapSensorEntry(entity, Attributes.SERVICE_PROBLEMS, sensor.getName()); + } + /** as {@link #updateProblemsIndicator(EntityLocal, Sensor, Object)} */ + public static void updateProblemsIndicator(EntityLocal entity, Effector<?> eff, Object value) { + updateMapSensorEntry(entity, Attributes.SERVICE_PROBLEMS, eff.getName(), value); + } + /** as {@link #clearProblemsIndicator(EntityLocal, Sensor)} */ + public static void clearProblemsIndicator(EntityLocal entity, Effector<?> eff) { + clearMapSensorEntry(entity, Attributes.SERVICE_PROBLEMS, eff.getName()); + } + } + + public static class ComputeServiceIndicatorsFromChildrenAndMembers extends AbstractMultipleSensorAggregator<Void> implements SensorEventListener<Object> { + /** standard unique tag identifying instances of this enricher at runtime, also used for the map sensor if no unique tag specified */ + public final static String IDENTIFIER_DEFAULT = "service-lifecycle-indicators-from-children-and-members"; + + /** as {@link #IDENTIFIER_LIFECYCLE}, but when a second distinct instance is responsible for computing service up */ + public final static String IDENTIFIER_UP = "service-not-up-indicators-from-children-and-members"; + + public static final ConfigKey<QuorumCheck> UP_QUORUM_CHECK = ConfigKeys.newConfigKey(QuorumCheck.class, "enricher.service_state.children_and_members.quorum.up", + "Logic for checking whether this service is up, based on children and/or members, defaulting to allowing none but if there are any requiring at least one to be up", QuorumCheck.QuorumChecks.atLeastOneUnlessEmpty()); + public static final ConfigKey<QuorumCheck> RUNNING_QUORUM_CHECK = ConfigKeys.newConfigKey(QuorumCheck.class, "enricher.service_state.children_and_members.quorum.running", + "Logic for checking whether this service is healthy, based on children and/or members running, defaulting to requiring none to be ON-FIRE", QuorumCheck.QuorumChecks.all()); + public static final ConfigKey<Boolean> DERIVE_SERVICE_NOT_UP = ConfigKeys.newBooleanConfigKey("enricher.service_state.children_and_members.service_up.publish", "Whether to derive a service-not-up indicator from children", true); + public static final ConfigKey<Boolean> DERIVE_SERVICE_PROBLEMS = ConfigKeys.newBooleanConfigKey("enricher.service_state.children_and_members.service_problems.publish", "Whether to derive a service-problem indicator from children", true); + + protected String getKeyForMapSensor() { + return Preconditions.checkNotNull(super.getUniqueTag()); + } + + @Override + protected void setEntityLoadingConfig() { + fromChildren = true; + fromMembers = true; + // above sets default + super.setEntityLoadingConfig(); + if (fromMembers && (!(entity instanceof Group))) { + if (fromChildren) fromMembers=false; + else throw new IllegalStateException("Cannot monitor only members for non-group entity "+entity+": "+this); + } + Preconditions.checkNotNull(getKeyForMapSensor()); + } + + protected void setEntityLoadingTargetConfig() { + if (getConfig(TARGET_SENSOR)!=null) + throw new IllegalArgumentException("Must not set "+TARGET_SENSOR+" when using "+this); + } + + public void setEntity(EntityLocal entity) { + super.setEntity(entity); + if (suppressDuplicates==null) { + // only publish on changes, unless it is configured otherwise + suppressDuplicates = Boolean.TRUE; + } + } + + private final List<Sensor<?>> SOURCE_SENSORS = ImmutableList.<Sensor<?>>of(SERVICE_UP, SERVICE_STATE_ACTUAL); + @Override + protected Collection<Sensor<?>> getSourceSensors() { + return SOURCE_SENSORS; + } + + @Override + protected void onUpdated() { + // override superclass to publish potentially several items + + if (getConfig(DERIVE_SERVICE_PROBLEMS)) + updateMapSensor(SERVICE_PROBLEMS, computeServiceProblems()); + + if (getConfig(DERIVE_SERVICE_NOT_UP)) + updateMapSensor(SERVICE_NOT_UP_INDICATORS, computeServiceNotUp()); + } + + protected Object computeServiceNotUp() { + Map<Entity, Boolean> values = getValues(SERVICE_UP); + List<Entity> violators = MutableList.of(); + for (Map.Entry<Entity, Boolean> state: values.entrySet()) { + if (!Boolean.TRUE.equals(state.getValue())) { + violators.add(state.getKey()); + } + } + + QuorumCheck qc = getConfig(UP_QUORUM_CHECK); + if (qc!=null) { + if (qc.isQuorate(values.size()-violators.size(), values.size())) + // quorate + return null; + + if (values.isEmpty()) return "No entities present"; + if (violators.isEmpty()) return "Not enough entities"; + } else { + if (violators.isEmpty()) + return null; + } + + if (violators.size()==1) return violators.get(0)+" is not up"; + if (violators.size()==values.size()) return "None of the entities are up"; + return violators.size()+" entities are not up, including "+violators.get(0); + } + + protected Object computeServiceProblems() { + Map<Entity, Lifecycle> values = getValues(SERVICE_STATE_ACTUAL); + int numRunning=0, numOnFire=0; + for (Lifecycle state: values.values()) { + if (state==Lifecycle.RUNNING) numRunning++; + else if (state==Lifecycle.ON_FIRE) numOnFire++; + } + + QuorumCheck qc = getConfig(RUNNING_QUORUM_CHECK); + if (qc!=null) { + if (qc.isQuorate(numRunning, numOnFire+numRunning)) + // quorate + return null; + + if (numOnFire==0) + return "Not enough entities running to be quorate"; + } else { + if (numOnFire==0) + return null; + } + + return numOnFire+" entit"+Strings.ies(numOnFire)+" are on fire"; + } + + protected void updateMapSensor(AttributeSensor<Map<String, Object>> sensor, Object value) { + if (value!=null) + updateMapSensorEntry(entity, sensor, getKeyForMapSensor(), value); + else + clearMapSensorEntry(entity, sensor, getKeyForMapSensor()); + } + + /** not used; see specific `computeXxx` methods, invoked by overridden onUpdated */ + @Override + protected Object compute() { + return null; + } + } + + public static class ComputeServiceIndicatorsFromChildrenAndMembersSpec extends ExtensibleEnricherSpec<ComputeServiceIndicatorsFromChildrenAndMembers,ComputeServiceIndicatorsFromChildrenAndMembersSpec> { + private static final long serialVersionUID = -607444925297963712L; + + protected ComputeServiceIndicatorsFromChildrenAndMembersSpec() { + this(ComputeServiceIndicatorsFromChildrenAndMembers.class); + } + + protected ComputeServiceIndicatorsFromChildrenAndMembersSpec(Class<? extends ComputeServiceIndicatorsFromChildrenAndMembers> clazz) { + super(clazz); + } + + public void addTo(Entity entity) { + entity.addEnricher(this); + } + + public ComputeServiceIndicatorsFromChildrenAndMembersSpec checkChildrenAndMembers() { + configure(ComputeServiceIndicatorsFromChildrenAndMembers.FROM_MEMBERS, true); + configure(ComputeServiceIndicatorsFromChildrenAndMembers.FROM_CHILDREN, true); + return self(); + } + public ComputeServiceIndicatorsFromChildrenAndMembersSpec checkMembersOnly() { + configure(ComputeServiceIndicatorsFromChildrenAndMembers.FROM_MEMBERS, true); + configure(ComputeServiceIndicatorsFromChildrenAndMembers.FROM_CHILDREN, false); + return self(); + } + public ComputeServiceIndicatorsFromChildrenAndMembersSpec checkChildrenOnly() { + configure(ComputeServiceIndicatorsFromChildrenAndMembers.FROM_MEMBERS, false); + configure(ComputeServiceIndicatorsFromChildrenAndMembers.FROM_CHILDREN, true); + return self(); + } + + public ComputeServiceIndicatorsFromChildrenAndMembersSpec requireUpChildren(QuorumCheck check) { + configure(ComputeServiceIndicatorsFromChildrenAndMembers.UP_QUORUM_CHECK, check); + return self(); + } + public ComputeServiceIndicatorsFromChildrenAndMembersSpec requireRunningChildren(QuorumCheck check) { + configure(ComputeServiceIndicatorsFromChildrenAndMembers.RUNNING_QUORUM_CHECK, check); + return self(); + } + } + + /** provides the default {@link ComputeServiceIndicatorsFromChildrenAndMembers} enricher, + * using the default unique tag ({@link ComputeServiceIndicatorsFromChildrenAndMembers#IDENTIFIER_DEFAULT}), + * configured here to require none on fire, and either no children or at least one up child, + * the spec can be further configured as appropriate */ + public static ComputeServiceIndicatorsFromChildrenAndMembersSpec newEnricherFromChildren() { + return new ComputeServiceIndicatorsFromChildrenAndMembersSpec() + .uniqueTag(ComputeServiceIndicatorsFromChildrenAndMembers.IDENTIFIER_DEFAULT); + } + + /** as {@link #newEnricherFromChildren()} but only publishing service not-up indicators, + * using a different unique tag ({@link ComputeServiceIndicatorsFromChildrenAndMembers#IDENTIFIER_UP}), + * listening to children only, ignoring lifecycle/service-state, + * and using the same logic + * (viz looking only at children (not members) and requiring either no children or at least one child up) by default */ + public static ComputeServiceIndicatorsFromChildrenAndMembersSpec newEnricherFromChildrenUp() { + return newEnricherFromChildren() + .uniqueTag(ComputeServiceIndicatorsFromChildrenAndMembers.IDENTIFIER_UP) + .checkChildrenOnly() + .configure(ComputeServiceIndicatorsFromChildrenAndMembers.DERIVE_SERVICE_PROBLEMS, false); + } + + /** as {@link #newEnricherFromChildren()} but only publishing service problems, + * listening to children and members, ignoring service up, + * and using the same logic + * (viz looking at children and members and requiring none are on fire) by default */ + public static ComputeServiceIndicatorsFromChildrenAndMembersSpec newEnricherFromChildrenState() { + return newEnricherFromChildren() + .configure(ComputeServiceIndicatorsFromChildrenAndMembers.DERIVE_SERVICE_NOT_UP, false); + } + +}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/core/src/main/java/brooklyn/entity/basic/ServiceStatusLogic.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/entity/basic/ServiceStatusLogic.java b/core/src/main/java/brooklyn/entity/basic/ServiceStatusLogic.java deleted file mode 100644 index 8f4c6b4..0000000 --- a/core/src/main/java/brooklyn/entity/basic/ServiceStatusLogic.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package brooklyn.entity.basic; - -import java.util.Map; - -import brooklyn.enricher.Enrichers; -import brooklyn.enricher.basic.UpdatingMap; -import brooklyn.event.AttributeSensor; -import brooklyn.event.Sensor; -import brooklyn.policy.EnricherSpec; -import brooklyn.util.collections.CollectionFunctionals; -import brooklyn.util.collections.MutableMap; -import brooklyn.util.guava.Functionals; - -import com.google.common.base.Function; -import com.google.common.base.Functions; - -/** Logic, sensors and enrichers, and conveniences, for computing service status */ -public class ServiceStatusLogic { - - public static final AttributeSensor<Boolean> SERVICE_UP = Attributes.SERVICE_UP; - public static final AttributeSensor<Map<String,Object>> SERVICE_NOT_UP_INDICATORS = Attributes.SERVICE_NOT_UP_INDICATORS; - - private ServiceStatusLogic() {} - - public static class ServiceNotUpLogic { - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static final EnricherSpec<?> newEnricherForServiceUpIfNoNotUpIndicators() { - return Enrichers.builder() - .transforming(SERVICE_NOT_UP_INDICATORS).publishing(Attributes.SERVICE_UP) - .computing( /* cast hacks to support removing */ (Function) - Functionals.<Map<String,?>> - ifNotEquals(null).<Object>apply(Functions.forPredicate(CollectionFunctionals.<String>mapSizeEquals(0))) - .defaultValue(Entities.REMOVE) ) - .uniqueTag("service.isUp if no service.notUp.indicators") - .build(); - } - - /** puts the given value into the {@link Attributes#SERVICE_NOT_UP_INDICATORS} map as if the - * {@link UpdatingMap} enricher for the given sensor reported this value (including {@link Entities#REMOVE}) */ - public static void updateMapFromSensor(EntityLocal entity, Sensor<?> sensor, Object value) { - updateMapSensor(entity, Attributes.SERVICE_NOT_UP_INDICATORS, sensor.getName(), value); - } - } - - - @SuppressWarnings("unchecked") - public static <TKey,TVal> void updateMapSensor(EntityLocal entity, AttributeSensor<Map<TKey,TVal>> sensor, - TKey key, Object v) { - Map<TKey, TVal> map = entity.getAttribute(sensor); - - // TODO synchronize - - boolean created = (map==null); - if (created) map = MutableMap.of(); - - boolean changed; - if (v == Entities.REMOVE) { - changed = map.containsKey(key); - if (changed) - map.remove(key); - } else { - TVal oldV = map.get(key); - if (oldV==null) - changed = (v!=null || !map.containsKey(key)); - else - changed = !oldV.equals(v); - if (changed) - map.put(key, (TVal)v); - } - if (changed || created) - entity.setAttribute(sensor, map); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/core/src/main/java/brooklyn/entity/group/DynamicCluster.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/entity/group/DynamicCluster.java b/core/src/main/java/brooklyn/entity/group/DynamicCluster.java index c44f302..f51a9ef 100644 --- a/core/src/main/java/brooklyn/entity/group/DynamicCluster.java +++ b/core/src/main/java/brooklyn/entity/group/DynamicCluster.java @@ -103,7 +103,7 @@ public interface DynamicCluster extends AbstractGroup, Cluster, MemberReplaceabl ConfigKey<Boolean> QUARANTINE_FAILED_ENTITIES = ConfigKeys.newBooleanConfigKey( "dynamiccluster.quarantineFailedEntities", "If true, will quarantine entities that fail to start; if false, will get rid of them (i.e. delete them)", true); - AttributeSensor<Lifecycle> SERVICE_STATE = Attributes.SERVICE_STATE; + AttributeSensor<Lifecycle> SERVICE_STATE_ACTUAL = Attributes.SERVICE_STATE_ACTUAL; BasicNotificationSensor<Entity> ENTITY_QUARANTINED = new BasicNotificationSensor<Entity>(Entity.class, "dynamiccluster.entityQuarantined", "Entity failed to start, and has been quarantined"); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/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 27ffaf8..1023db6 100644 --- a/core/src/main/java/brooklyn/entity/group/DynamicClusterImpl.java +++ b/core/src/main/java/brooklyn/entity/group/DynamicClusterImpl.java @@ -23,7 +23,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.util.Collection; import java.util.Collections; -import java.util.EnumSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -40,14 +39,13 @@ import brooklyn.entity.basic.AbstractGroupImpl; import brooklyn.entity.basic.Entities; import brooklyn.entity.basic.EntityFactory; import brooklyn.entity.basic.EntityFactoryForLocation; -import brooklyn.entity.basic.EntityFunctions; import brooklyn.entity.basic.Lifecycle; +import brooklyn.entity.basic.QuorumCheck.QuorumChecks; +import brooklyn.entity.basic.ServiceStateLogic; import brooklyn.entity.effector.Effectors; import brooklyn.entity.proxying.EntitySpec; import brooklyn.entity.trait.Startable; import brooklyn.entity.trait.StartableMethods; -import brooklyn.event.SensorEvent; -import brooklyn.event.SensorEventListener; import brooklyn.location.Location; import brooklyn.location.basic.Locations; import brooklyn.location.cloud.AvailabilityZoneExtension; @@ -147,6 +145,17 @@ public class DynamicClusterImpl extends AbstractGroupImpl implements DynamicClus } @Override + protected void initEnrichers() { + if (getConfigRaw(UP_QUORUM_CHECK, true).isAbsent() && getConfig(INITIAL_SIZE)==0) { + // if initial size is 0 then override up check to allow zero if empty + setConfig(UP_QUORUM_CHECK, QuorumChecks.atLeastOneUnlessEmpty()); + } + super.initEnrichers(); + // override previous enricher so that only members are checked + ServiceStateLogic.newEnricherFromChildrenUp().checkMembersOnly().requireUpChildren(getConfig(UP_QUORUM_CHECK)).addTo(this); + } + + @Override public void setRemovalStrategy(Function<Collection<Entity>, Entity> val) { setConfig(REMOVAL_STRATEGY, checkNotNull(val, "removalStrategy")); } @@ -247,13 +256,15 @@ public class DynamicClusterImpl extends AbstractGroupImpl implements DynamicClus setAttribute(SUB_LOCATIONS, findSubLocations(loc)); } - setAttribute(SERVICE_STATE, Lifecycle.STARTING); - setAttribute(SERVICE_UP, calculateServiceUp()); + ServiceStateLogic.setExpectedState(this, Lifecycle.STARTING); try { if (isQuarantineEnabled()) { - QuarantineGroup quarantineGroup = addChild(EntitySpec.create(QuarantineGroup.class).displayName("quarantine")); - Entities.manage(quarantineGroup); - setAttribute(QUARANTINE_GROUP, quarantineGroup); + QuarantineGroup quarantineGroup = getAttribute(QUARANTINE_GROUP); + if (quarantineGroup==null || !Entities.isManaged(quarantineGroup)) { + quarantineGroup = addChild(EntitySpec.create(QuarantineGroup.class).displayName("quarantine")); + Entities.manage(quarantineGroup); + setAttribute(QUARANTINE_GROUP, quarantineGroup); + } } int initialSize = getConfig(INITIAL_SIZE).intValue(); @@ -307,44 +318,11 @@ public class DynamicClusterImpl extends AbstractGroupImpl implements DynamicClus for (Policy it : getPolicies()) { it.resume(); } - setAttribute(SERVICE_STATE, Lifecycle.RUNNING); - setAttribute(SERVICE_UP, calculateServiceUp()); + ServiceStateLogic.setExpectedState(this, Lifecycle.RUNNING); } catch (Exception e) { - setAttribute(SERVICE_STATE, Lifecycle.ON_FIRE); + ServiceStateLogic.setExpectedState(this, Lifecycle.ON_FIRE); throw Exceptions.propagate(e); - } finally { - connectSensors(); - } - } - - protected void connectSensors() { - subscribeToChildren(this, SERVICE_STATE, new SensorEventListener<Lifecycle>() { - @Override - public void onEvent(SensorEvent<Lifecycle> event) { - setAttribute(SERVICE_STATE, calculateServiceState()); - } - }); - subscribeToChildren(this, SERVICE_UP, new SensorEventListener<Boolean>() { - @Override - public void onEvent(SensorEvent<Boolean> event) { - setAttribute(SERVICE_UP, calculateServiceUp()); - } - }); - } - - protected Lifecycle calculateServiceState() { - Lifecycle currentState = getAttribute(SERVICE_STATE); - if (EnumSet.of(Lifecycle.ON_FIRE, Lifecycle.RUNNING).contains(currentState)) { - Iterable<Lifecycle> memberStates = Iterables.transform(getMembers(), EntityFunctions.attribute(SERVICE_STATE)); - int running = Iterables.frequency(memberStates, Lifecycle.RUNNING); - int onFire = Iterables.frequency(memberStates, Lifecycle.ON_FIRE); - if ((getInitialQuorumSize() > 0 ? running < getInitialQuorumSize() : true) && onFire > 0) { - currentState = Lifecycle.ON_FIRE; - } else if (onFire == 0 && running > 0) { - currentState = Lifecycle.RUNNING; - } } - return currentState; } protected List<Location> findSubLocations(Location loc) { @@ -386,10 +364,8 @@ public class DynamicClusterImpl extends AbstractGroupImpl implements DynamicClus @Override public void stop() { - setAttribute(SERVICE_STATE, Lifecycle.STOPPING); + ServiceStateLogic.setExpectedState(this, Lifecycle.STOPPING); try { - setAttribute(SERVICE_UP, calculateServiceUp()); - for (Policy it : getPolicies()) { it.suspend(); } // run shrink without mutex to make things stop even if starting, @@ -403,10 +379,9 @@ public class DynamicClusterImpl extends AbstractGroupImpl implements DynamicClus // (this ignores the quarantine node which is not stoppable) StartableMethods.stop(this); - setAttribute(SERVICE_STATE, Lifecycle.STOPPED); - setAttribute(SERVICE_UP, calculateServiceUp()); + ServiceStateLogic.setExpectedState(this, Lifecycle.STOPPED); } catch (Exception e) { - setAttribute(SERVICE_STATE, Lifecycle.ON_FIRE); + ServiceStateLogic.setExpectedState(this, Lifecycle.ON_FIRE); throw Exceptions.propagate(e); } } @@ -697,13 +672,6 @@ public class DynamicClusterImpl extends AbstractGroupImpl implements DynamicClus } } - /** - * Default impl is to be up when running, and !up otherwise. - */ - protected boolean calculateServiceUp() { - return getAttribute(SERVICE_STATE) == Lifecycle.RUNNING; - } - protected Map<Entity, Throwable> waitForTasksOnEntityStart(Map<? extends Entity,? extends Task<?>> tasks) { // TODO Could have CompoundException, rather than propagating first Map<Entity, Throwable> errors = Maps.newLinkedHashMap(); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/core/src/main/java/brooklyn/entity/group/DynamicFabric.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/entity/group/DynamicFabric.java b/core/src/main/java/brooklyn/entity/group/DynamicFabric.java index abd7f5f..51735ec 100644 --- a/core/src/main/java/brooklyn/entity/group/DynamicFabric.java +++ b/core/src/main/java/brooklyn/entity/group/DynamicFabric.java @@ -65,7 +65,7 @@ public interface DynamicFabric extends AbstractGroup, Startable, Fabric { public static final MapConfigKey<Object> CUSTOM_CHILD_FLAGS = new MapConfigKey<Object>( Object.class, "dynamicfabric.customChildFlags", "Additional flags to be passed to children when they are being created", ImmutableMap.<String,Object>of()); - public static final AttributeSensor<Lifecycle> SERVICE_STATE = Attributes.SERVICE_STATE; + public static final AttributeSensor<Lifecycle> SERVICE_STATE_ACTUAL = Attributes.SERVICE_STATE_ACTUAL; public void setMemberSpec(EntitySpec<?> memberSpec); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/core/src/main/java/brooklyn/entity/group/DynamicFabricImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/entity/group/DynamicFabricImpl.java b/core/src/main/java/brooklyn/entity/group/DynamicFabricImpl.java index a72f77f..8ba08d0 100644 --- a/core/src/main/java/brooklyn/entity/group/DynamicFabricImpl.java +++ b/core/src/main/java/brooklyn/entity/group/DynamicFabricImpl.java @@ -39,6 +39,7 @@ import brooklyn.entity.basic.EntityFactoryForLocation; import brooklyn.entity.basic.EntityInternal; import brooklyn.entity.basic.EntityLocal; import brooklyn.entity.basic.Lifecycle; +import brooklyn.entity.basic.ServiceStateLogic; import brooklyn.entity.effector.Effectors; import brooklyn.entity.proxying.EntitySpec; import brooklyn.entity.trait.Changeable; @@ -118,7 +119,7 @@ public class DynamicFabricImpl extends AbstractGroupImpl implements DynamicFabri if (newLocations.isEmpty()) newLocations.addAll(getLocations()); int locIndex = 0; - setAttribute(SERVICE_STATE, Lifecycle.STARTING); + ServiceStateLogic.setExpectedState(this, Lifecycle.STARTING); try { Map<Entity, Task<?>> tasks = Maps.newLinkedHashMap(); @@ -158,10 +159,10 @@ public class DynamicFabricImpl extends AbstractGroupImpl implements DynamicFabri } waitForTasksOnStart(tasks); - setAttribute(SERVICE_STATE, Lifecycle.RUNNING); + ServiceStateLogic.setExpectedState(this, Lifecycle.RUNNING); setAttribute(SERVICE_UP, true); } catch (Exception e) { - setAttribute(SERVICE_STATE, Lifecycle.ON_FIRE); + ServiceStateLogic.setExpectedState(this, Lifecycle.ON_FIRE); throw Exceptions.propagate(e); } } @@ -183,15 +184,15 @@ public class DynamicFabricImpl extends AbstractGroupImpl implements DynamicFabri @Override public void stop() { - setAttribute(SERVICE_STATE, Lifecycle.STOPPING); + ServiceStateLogic.setExpectedState(this, Lifecycle.STOPPING); try { Iterable<Entity> stoppableChildren = Iterables.filter(getChildren(), Predicates.instanceOf(Startable.class)); Task<?> invoke = Entities.invokeEffector(this, stoppableChildren, Startable.STOP); if (invoke != null) invoke.get(); - setAttribute(SERVICE_STATE, Lifecycle.STOPPED); + ServiceStateLogic.setExpectedState(this, Lifecycle.STOPPED); setAttribute(SERVICE_UP, false); } catch (Exception e) { - setAttribute(SERVICE_STATE, Lifecycle.ON_FIRE); + ServiceStateLogic.setExpectedState(this, Lifecycle.ON_FIRE); throw Exceptions.propagate(e); } } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/core/src/main/java/brooklyn/event/basic/BasicConfigKey.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/event/basic/BasicConfigKey.java b/core/src/main/java/brooklyn/event/basic/BasicConfigKey.java index 5bb864d..91420e5 100644 --- a/core/src/main/java/brooklyn/event/basic/BasicConfigKey.java +++ b/core/src/main/java/brooklyn/event/basic/BasicConfigKey.java @@ -227,8 +227,12 @@ public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializab private final ConfigKey<T> parentKey; public BasicConfigKeyOverwriting(ConfigKey<T> key, T defaultValue) { + this(key, key.getDescription(), defaultValue); + } + + public BasicConfigKeyOverwriting(ConfigKey<T> key, String newDescription, T defaultValue) { super(checkNotNull(key.getTypeToken(), "type"), checkNotNull(key.getName(), "name"), - key.getDescription(), defaultValue); + newDescription, defaultValue); parentKey = key; } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/core/src/main/java/brooklyn/event/basic/DependentConfiguration.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/event/basic/DependentConfiguration.java b/core/src/main/java/brooklyn/event/basic/DependentConfiguration.java index 8630214..9d8bf4b 100644 --- a/core/src/main/java/brooklyn/event/basic/DependentConfiguration.java +++ b/core/src/main/java/brooklyn/event/basic/DependentConfiguration.java @@ -401,12 +401,12 @@ public class DependentConfiguration { /** * Will wait for the attribute on the given entity. - * If that entity report {@link Lifecycle#ON_FIRE} for its {@link Attributes#SERVICE_STATE} then it will abort. + * If that entity report {@link Lifecycle#ON_FIRE} for its {@link Attributes#SERVICE_STATE_ACTUAL} then it will abort. */ public <T2> Builder<T2,T2> attributeWhenReady(Entity source, AttributeSensor<T2> sensor) { this.source = checkNotNull(source, "source"); this.sensor = (AttributeSensor) checkNotNull(sensor, "sensor"); - abortIf(source, Attributes.SERVICE_STATE, Predicates.equalTo(Lifecycle.ON_FIRE)); + abortIf(source, Attributes.SERVICE_STATE_ACTUAL, Predicates.equalTo(Lifecycle.ON_FIRE)); return (Builder<T2, T2>) this; } /** returns a task for parallel execution returning a list of values of the given sensor list on the given entity, http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/core/src/main/java/brooklyn/policy/basic/AbstractEntityAdjunct.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/policy/basic/AbstractEntityAdjunct.java b/core/src/main/java/brooklyn/policy/basic/AbstractEntityAdjunct.java index 68f8162..6b4d49d 100644 --- a/core/src/main/java/brooklyn/policy/basic/AbstractEntityAdjunct.java +++ b/core/src/main/java/brooklyn/policy/basic/AbstractEntityAdjunct.java @@ -377,6 +377,7 @@ public abstract class AbstractEntityAdjunct extends AbstractBrooklynObject imple .add("name", name) .add("uniqueTag", uniqueTag) .add("running", isRunning()) + .add("entity", entity) .add("id", getId()) .toString(); } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/core/src/test/java/brooklyn/enricher/basic/BasicEnricherTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/enricher/basic/BasicEnricherTest.java b/core/src/test/java/brooklyn/enricher/basic/BasicEnricherTest.java index 1252e36..59bc7e8 100644 --- a/core/src/test/java/brooklyn/enricher/basic/BasicEnricherTest.java +++ b/core/src/test/java/brooklyn/enricher/basic/BasicEnricherTest.java @@ -22,11 +22,15 @@ import static org.testng.Assert.assertEquals; import java.util.Map; +import org.testng.Assert; import org.testng.annotations.Test; +import com.google.common.collect.Iterables; + import brooklyn.config.ConfigKey; import brooklyn.entity.BrooklynAppUnitTestSupport; import brooklyn.event.basic.BasicConfigKey; +import brooklyn.policy.Enricher; import brooklyn.policy.EnricherSpec; import brooklyn.util.collections.MutableSet; import brooklyn.util.flags.SetFromFlag; @@ -91,10 +95,14 @@ public class BasicEnricherTest extends BrooklynAppUnitTestSupport { @Test public void testSameUniqueTagEnricherNotAddedTwice() throws Exception { - MyEnricher enricher1 = app.addEnricher(EnricherSpec.create(MyEnricher.class).tag(99).uniqueTag("x")); - MyEnricher enricher2 = app.addEnricher(EnricherSpec.create(MyEnricher.class).tag(94).uniqueTag("x")); - assertEquals(enricher2, enricher1); + app.addEnricher(EnricherSpec.create(MyEnricher.class).tag(99).uniqueTag("x")); + app.addEnricher(EnricherSpec.create(MyEnricher.class).tag(94).uniqueTag("x")); + assertEquals(app.getEnrichers().size(), 1); + // the more recent one should dominate + Enricher enricher = Iterables.getOnlyElement(app.getEnrichers()); + Assert.assertTrue(enricher.getTagSupport().containsTag(94)); + Assert.assertFalse(enricher.getTagSupport().containsTag(99)); } } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/core/src/test/java/brooklyn/entity/basic/DependentConfigurationTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/entity/basic/DependentConfigurationTest.java b/core/src/test/java/brooklyn/entity/basic/DependentConfigurationTest.java index 3c4acbb..f6096a4 100644 --- a/core/src/test/java/brooklyn/entity/basic/DependentConfigurationTest.java +++ b/core/src/test/java/brooklyn/entity/basic/DependentConfigurationTest.java @@ -192,7 +192,7 @@ public class DependentConfigurationTest extends BrooklynAppUnitTestSupport { .attributeWhenReady(entity, TestEntity.NAME) .build()); - entity.setAttribute(Attributes.SERVICE_STATE, Lifecycle.ON_FIRE); + ServiceStateLogic.setExpectedState(entity, Lifecycle.ON_FIRE); try { assertDoneEventually(t); fail(); @@ -203,7 +203,7 @@ public class DependentConfigurationTest extends BrooklynAppUnitTestSupport { @Test public void testAttributeWhenReadyAbortsWhenAlreadyOnfireByDefault() throws Exception { - entity.setAttribute(Attributes.SERVICE_STATE, Lifecycle.ON_FIRE); + ServiceStateLogic.setExpectedState(entity, Lifecycle.ON_FIRE); final Task<String> t = submit(DependentConfiguration.builder() .attributeWhenReady(entity, TestEntity.NAME) http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/core/src/test/java/brooklyn/entity/basic/DynamicGroupTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/entity/basic/DynamicGroupTest.java b/core/src/test/java/brooklyn/entity/basic/DynamicGroupTest.java index 12c3b55..b52332b 100644 --- a/core/src/test/java/brooklyn/entity/basic/DynamicGroupTest.java +++ b/core/src/test/java/brooklyn/entity/basic/DynamicGroupTest.java @@ -369,7 +369,6 @@ public class DynamicGroupTest { }; ((EntityLocal)group2).setConfig(DynamicGroup.ENTITY_FILTER, Predicates.instanceOf(TestEntity.class)); app.addChild(group2); - group2.init(); Entities.manage(group2); for (int i = 0; i < NUM_CYCLES; i++) { @@ -421,7 +420,6 @@ public class DynamicGroupTest { }; ((EntityLocal)group2).setConfig(DynamicGroup.ENTITY_FILTER, Predicates.<Object>equalTo(e3)); app.addChild(group2); - group2.init(); Thread t1 = new Thread(new Runnable() { @Override public void run() { http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java b/core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java index 6363a81..0a86e31 100644 --- a/core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java +++ b/core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java @@ -37,6 +37,7 @@ import brooklyn.policy.PolicySpec; import brooklyn.policy.basic.AbstractPolicy; import brooklyn.test.Asserts; import brooklyn.test.entity.TestEntity; +import brooklyn.test.entity.TestEntityNoEnrichersImpl; import brooklyn.util.flags.SetFromFlag; import com.google.common.collect.ImmutableSet; @@ -104,7 +105,7 @@ public class EntitySpecTest extends BrooklynAppUnitTestSupport { @Test public void testAddsEnricherSpec() throws Exception { - entity = app.createAndManageChild(EntitySpec.create(TestEntity.class) + entity = app.createAndManageChild(EntitySpec.create(TestEntity.class, TestEntityNoEnrichersImpl.class) .enricher(EnricherSpec.create(MyEnricher.class) .displayName("myenrichername") .configure(MyEnricher.CONF1, "myconf1val") @@ -119,7 +120,7 @@ public class EntitySpecTest extends BrooklynAppUnitTestSupport { @Test public void testAddsEnricher() throws Exception { MyEnricher enricher = new MyEnricher(); - entity = app.createAndManageChild(EntitySpec.create(TestEntity.class) + entity = app.createAndManageChild(EntitySpec.create(TestEntity.class, TestEntityNoEnrichersImpl.class) .enricher(enricher)); assertEquals(Iterables.getOnlyElement(entity.getEnrichers()), enricher); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/core/src/test/java/brooklyn/entity/basic/PolicyRegistrationTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/entity/basic/PolicyRegistrationTest.java b/core/src/test/java/brooklyn/entity/basic/PolicyRegistrationTest.java index ae6b384..168f72c 100644 --- a/core/src/test/java/brooklyn/entity/basic/PolicyRegistrationTest.java +++ b/core/src/test/java/brooklyn/entity/basic/PolicyRegistrationTest.java @@ -38,6 +38,7 @@ import brooklyn.policy.PolicySpec; import brooklyn.policy.basic.AbstractPolicy; import brooklyn.test.TestUtils; import brooklyn.test.entity.TestEntity; +import brooklyn.test.entity.TestEntityNoEnrichersImpl; import brooklyn.util.collections.MutableMap; import com.google.common.collect.ImmutableList; @@ -116,9 +117,10 @@ public class PolicyRegistrationTest extends BrooklynAppUnitTestSupport { @Test public void testAddEnricherSpec() { - EntitySpecTest.MyEnricher enricher = entity.addEnricher(EnricherSpec.create(EntitySpecTest.MyEnricher.class)); + TestEntity entity2 = app.createAndManageChild(EntitySpec.create(TestEntity.class, TestEntityNoEnrichersImpl.class)); + EntitySpecTest.MyEnricher enricher = entity2.addEnricher(EnricherSpec.create(EntitySpecTest.MyEnricher.class)); assertNotNull(enricher); - assertEquals(entity.getEnrichers(), ImmutableList.of(enricher)); + assertEquals(entity2.getEnrichers(), ImmutableList.of(enricher)); } @Test http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/core/src/test/java/brooklyn/entity/group/DynamicClusterTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/entity/group/DynamicClusterTest.java b/core/src/test/java/brooklyn/entity/group/DynamicClusterTest.java index c862f8a..ed1d7b6 100644 --- a/core/src/test/java/brooklyn/entity/group/DynamicClusterTest.java +++ b/core/src/test/java/brooklyn/entity/group/DynamicClusterTest.java @@ -57,10 +57,12 @@ import brooklyn.location.Location; import brooklyn.location.basic.SimulatedLocation; import brooklyn.management.Task; import brooklyn.test.Asserts; +import brooklyn.test.EntityTestUtils; import brooklyn.test.entity.TestEntity; import brooklyn.test.entity.TestEntityImpl; import brooklyn.util.collections.MutableMap; import brooklyn.util.exceptions.Exceptions; +import brooklyn.util.time.Duration; import brooklyn.util.time.Time; import com.google.common.base.Function; @@ -143,7 +145,8 @@ public class DynamicClusterTest extends BrooklynAppUnitTestSupport { .configure(DynamicCluster.MEMBER_SPEC, EntitySpec.create(TestEntity.class)) .configure(DynamicCluster.INITIAL_SIZE, 0)); cluster.start(ImmutableList.of(loc)); - assertEquals(cluster.getAttribute(Attributes.SERVICE_STATE), Lifecycle.RUNNING); + + EntityTestUtils.assertAttributeEqualsEventually(cluster, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); assertTrue(cluster.getAttribute(Attributes.SERVICE_UP)); } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/core/src/test/java/brooklyn/entity/rebind/RebindEnricherTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindEnricherTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindEnricherTest.java index 27e5edc..fc89ec3 100644 --- a/core/src/test/java/brooklyn/entity/rebind/RebindEnricherTest.java +++ b/core/src/test/java/brooklyn/entity/rebind/RebindEnricherTest.java @@ -286,8 +286,8 @@ public class RebindEnricherTest extends RebindTestFixtureWithApp { public static class MyTestEntityWithEnricher extends TestEntityImpl { @Override - public void init() { - super.init(); + protected void initEnrichers() { + // don't add default ones addEnricher(EnricherSpec.create(MyEnricher.class).uniqueTag("x").tag(Identifiers.makeRandomId(8))); addEnricher(EnricherSpec.create(MyEnricher.class)); } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/core/src/test/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterTestFixture.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterTestFixture.java b/core/src/test/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterTestFixture.java index 4b22592..c71eaa8 100644 --- a/core/src/test/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterTestFixture.java +++ b/core/src/test/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterTestFixture.java @@ -122,7 +122,7 @@ public abstract class BrooklynMementoPersisterTestFixture { assertTrue(Iterables.contains(reloadedMemento.getEntityIds(), entity.getId())); assertEquals(Iterables.getOnlyElement(reloadedMemento.getLocationIds()), location.getId()); assertEquals(Iterables.getOnlyElement(reloadedMemento.getPolicyIds()), policy.getId()); - assertEquals(Iterables.getOnlyElement(reloadedMemento.getEnricherIds()), enricher.getId()); + assertTrue(reloadedMemento.getEnricherIds().contains(enricher.getId())); } @Test http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/core/src/test/java/brooklyn/event/feed/http/HttpFeedTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/event/feed/http/HttpFeedTest.java b/core/src/test/java/brooklyn/event/feed/http/HttpFeedTest.java index 1a9cf9f..f112890 100644 --- a/core/src/test/java/brooklyn/event/feed/http/HttpFeedTest.java +++ b/core/src/test/java/brooklyn/event/feed/http/HttpFeedTest.java @@ -47,6 +47,7 @@ import brooklyn.test.Asserts; import brooklyn.test.entity.TestEntity; import brooklyn.util.collections.MutableList; import brooklyn.util.collections.MutableMap; +import brooklyn.util.guava.Functionals; import brooklyn.util.http.BetterMockWebServer; import brooklyn.util.http.HttpToolResponse; import brooklyn.util.time.Duration; @@ -381,9 +382,9 @@ public class HttpFeedTest extends BrooklynAppUnitTestSupport { return null; } }) - .onFailureOrException(EntityFunctions.settingSensorsConstantFunction(entity, MutableMap.<AttributeSensor<?>,Object>of( + .onFailureOrException(Functionals.function(EntityFunctions.settingSensorsConstant(entity, MutableMap.<AttributeSensor<?>,Object>of( SENSOR_INT, -1, - SENSOR_STRING, PollConfig.REMOVE))) + SENSOR_STRING, PollConfig.REMOVE)))) .period(100)) .build(); } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/core/src/test/java/brooklyn/qa/longevity/EntityCleanupLongevityTestFixture.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/qa/longevity/EntityCleanupLongevityTestFixture.java b/core/src/test/java/brooklyn/qa/longevity/EntityCleanupLongevityTestFixture.java index fab5aa3..c3e6dce 100644 --- a/core/src/test/java/brooklyn/qa/longevity/EntityCleanupLongevityTestFixture.java +++ b/core/src/test/java/brooklyn/qa/longevity/EntityCleanupLongevityTestFixture.java @@ -131,7 +131,7 @@ public abstract class EntityCleanupLongevityTestFixture { // TODO would like to assert this // Assert.assertTrue( schedulers.isEmpty(), "Not empty schedulers: "+schedulers); // but weaker form for now - Assert.assertTrue( schedulers.size() <= iterations, "Not empty schedulers: "+schedulers); + Assert.assertTrue( schedulers.size() <= 3*iterations, "Not empty schedulers: "+schedulers.size()+" after "+iterations+", "+schedulers); // memory leak detection only applies to subclasses who run lots of iterations if (checkMemoryLeaks()) http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/core/src/test/java/brooklyn/test/entity/TestEntityImpl.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/test/entity/TestEntityImpl.java b/core/src/test/java/brooklyn/test/entity/TestEntityImpl.java index 64131f5..c2a3884 100644 --- a/core/src/test/java/brooklyn/test/entity/TestEntityImpl.java +++ b/core/src/test/java/brooklyn/test/entity/TestEntityImpl.java @@ -69,7 +69,7 @@ public class TestEntityImpl extends AbstractEntity implements TestEntity { return super.configure(flags); } - @Override + @Override // made public for testing public boolean isLegacyConstruction() { return super.isLegacyConstruction(); } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/core/src/test/java/brooklyn/test/entity/TestEntityNoEnrichersImpl.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/test/entity/TestEntityNoEnrichersImpl.java b/core/src/test/java/brooklyn/test/entity/TestEntityNoEnrichersImpl.java new file mode 100644 index 0000000..66ef7ae --- /dev/null +++ b/core/src/test/java/brooklyn/test/entity/TestEntityNoEnrichersImpl.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package brooklyn.test.entity; + + +/** + * Mock entity for testing. + */ +public class TestEntityNoEnrichersImpl extends TestEntityImpl { + + @Override + protected void initEnrichers() { + // no enrichers here, so we can test the explicit enrichers we set + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/CumulusRDFApplication.java ---------------------------------------------------------------------- diff --git a/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/CumulusRDFApplication.java b/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/CumulusRDFApplication.java index 46b00bb..a54ef58 100644 --- a/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/CumulusRDFApplication.java +++ b/examples/simple-nosql-cluster/src/main/java/brooklyn/demo/CumulusRDFApplication.java @@ -36,8 +36,10 @@ import brooklyn.entity.basic.ConfigKeys; import brooklyn.entity.basic.Entities; import brooklyn.entity.basic.EntityInternal; import brooklyn.entity.basic.Lifecycle; +import brooklyn.entity.basic.ServiceStateLogic; import brooklyn.entity.basic.SoftwareProcess; import brooklyn.entity.basic.StartableApplication; +import brooklyn.entity.basic.ServiceStateLogic.ServiceNotUpLogic; import brooklyn.entity.effector.EffectorBody; import brooklyn.entity.effector.Effectors; import brooklyn.entity.java.UsesJava; @@ -115,6 +117,8 @@ public class CumulusRDFApplication extends AbstractApplication { */ @Override public void init() { + super.init(); + // Cassandra cluster EntitySpec<CassandraDatacenter> clusterSpec = EntitySpec.create(CassandraDatacenter.class) .configure(CassandraDatacenter.MEMBER_SPEC, EntitySpec.create(CassandraNode.class) @@ -204,17 +208,15 @@ public class CumulusRDFApplication extends AbstractApplication { // TODO use a multi-region web cluster Collection<? extends Location> first = MutableList.copyOf(Iterables.limit(locations, 1)); - setAttribute(Attributes.SERVICE_STATE, Lifecycle.STARTING); + ServiceStateLogic.setExpectedState(this, Lifecycle.STARTING); try { Entities.invokeEffector(this, cassandra, Startable.START, MutableMap.of("locations", locations)).getUnchecked(); Entities.invokeEffector(this, webapp, Startable.START, MutableMap.of("locations", first)).getUnchecked(); } catch (Exception e) { - setAttribute(Attributes.SERVICE_STATE, Lifecycle.ON_FIRE); throw Exceptions.propagate(e); + } finally { + ServiceStateLogic.setExpectedState(this, Lifecycle.RUNNING); } - setAttribute(SERVICE_UP, true); - setAttribute(Attributes.SERVICE_STATE, Lifecycle.RUNNING); - log.info("Started CumulusRDF in " + locations); } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterDatabaseExample.java ---------------------------------------------------------------------- diff --git a/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterDatabaseExample.java b/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterDatabaseExample.java index f1ed949..bfd3a1c 100644 --- a/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterDatabaseExample.java +++ b/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterDatabaseExample.java @@ -70,6 +70,8 @@ public class WebClusterDatabaseExample extends AbstractApplication { @Override public void init() { + super.init(); + MySqlNode mysql = addChild(EntitySpec.create(MySqlNode.class) .configure("creationScriptUrl", DB_SETUP_SQL_URL)); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterDatabaseExampleApp.java ---------------------------------------------------------------------- diff --git a/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterDatabaseExampleApp.java b/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterDatabaseExampleApp.java index 672158e..b1d3915 100644 --- a/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterDatabaseExampleApp.java +++ b/examples/simple-web-cluster/src/main/java/brooklyn/demo/WebClusterDatabaseExampleApp.java @@ -118,6 +118,8 @@ public class WebClusterDatabaseExampleApp extends AbstractApplication implements @Override public void init() { + super.init(); + MySqlNode mysql = addChild( EntitySpec.create(MySqlNode.class) .configure(MySqlNode.CREATION_SCRIPT_URL, Entities.getRequiredUrlConfig(this, DB_SETUP_SQL_URL))); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessDriver.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessDriver.java b/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessDriver.java index d275418..e9ab449 100644 --- a/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessDriver.java +++ b/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessDriver.java @@ -136,7 +136,7 @@ public abstract class AbstractSoftwareProcessDriver implements SoftwareProcessDr DynamicTasks.markInessential(); boolean previouslyRunning = isRunning(); try { - getEntity().setAttribute(Attributes.SERVICE_STATE, Lifecycle.STOPPING); + ServiceStateLogic.setExpectedState(getEntity(), Lifecycle.STOPPING); stop(); } catch (Exception e) { // queue a failed task so that there is visual indication that this task had a failure, @@ -154,11 +154,11 @@ public abstract class AbstractSoftwareProcessDriver implements SoftwareProcessDr if (doFullStartOnRestart()) { DynamicTasks.waitForLast(); - getEntity().setAttribute(Attributes.SERVICE_STATE, Lifecycle.STARTING); + ServiceStateLogic.setExpectedState(getEntity(), Lifecycle.STARTING); start(); } else { DynamicTasks.queue("launch", new Runnable() { public void run() { - getEntity().setAttribute(Attributes.SERVICE_STATE, Lifecycle.STARTING); + ServiceStateLogic.setExpectedState(getEntity(), Lifecycle.STARTING); launch(); }}); DynamicTasks.queue("post-launch", new Runnable() { public void run() { http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/base/src/main/java/brooklyn/entity/basic/SameServerEntity.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/basic/SameServerEntity.java b/software/base/src/main/java/brooklyn/entity/basic/SameServerEntity.java index 05fab5e..f7ddddd 100644 --- a/software/base/src/main/java/brooklyn/entity/basic/SameServerEntity.java +++ b/software/base/src/main/java/brooklyn/entity/basic/SameServerEntity.java @@ -51,7 +51,7 @@ public interface SameServerEntity extends Entity, Startable { "provisioning.properties", "Custom properties to be passed in when provisioning a new machine", MutableMap.<String, Object>of()); - AttributeSensor<Lifecycle> SERVICE_STATE = Attributes.SERVICE_STATE; + AttributeSensor<Lifecycle> SERVICE_STATE_ACTUAL = Attributes.SERVICE_STATE_ACTUAL; @SuppressWarnings("rawtypes") AttributeSensor<MachineProvisioningLocation> PROVISIONING_LOCATION = new BasicAttributeSensor<MachineProvisioningLocation>( http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcess.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcess.java b/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcess.java index eb90660..0551840 100644 --- a/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcess.java +++ b/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcess.java @@ -156,7 +156,7 @@ public interface SoftwareProcess extends Entity, Startable { public static final AttributeSensor<Boolean> SERVICE_PROCESS_IS_RUNNING = Sensors.newBooleanSensor("service.process.isRunning", "Whether the process for the service is confirmed as running"); - public static final AttributeSensor<Lifecycle> SERVICE_STATE = Attributes.SERVICE_STATE; + public static final AttributeSensor<Lifecycle> SERVICE_STATE_ACTUAL = Attributes.SERVICE_STATE_ACTUAL; public static final AttributeSensor<String> PID_FILE = Sensors.newStringSensor("softwareprocess.pid.file", "PID file"); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcessDriverLifecycleEffectorTasks.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcessDriverLifecycleEffectorTasks.java b/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcessDriverLifecycleEffectorTasks.java index 797d4a3..60e3eb0 100644 --- a/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcessDriverLifecycleEffectorTasks.java +++ b/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcessDriverLifecycleEffectorTasks.java @@ -63,8 +63,7 @@ public class SoftwareProcessDriverLifecycleEffectorTasks extends MachineLifecycl entity().getDriver().restart(); DynamicTasks.queue("post-restart", new Runnable() { public void run() { postStartCustom(); - if (entity().getAttribute(Attributes.SERVICE_STATE) == Lifecycle.STARTING) - entity().setAttribute(Attributes.SERVICE_STATE, Lifecycle.RUNNING); + ServiceStateLogic.setExpectedState(entity(), Lifecycle.RUNNING); }}); } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcessImpl.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcessImpl.java b/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcessImpl.java index 25c23df..e264dab 100644 --- a/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcessImpl.java +++ b/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcessImpl.java @@ -34,7 +34,6 @@ import org.slf4j.LoggerFactory; import brooklyn.config.ConfigKey; import brooklyn.enricher.Enrichers; import brooklyn.entity.Entity; -import brooklyn.entity.basic.ServiceStatusLogic.ServiceNotUpLogic; import brooklyn.entity.drivers.DriverDependentEntity; import brooklyn.entity.drivers.EntityDriverManager; import brooklyn.event.feed.function.FunctionFeed; @@ -116,17 +115,14 @@ public abstract class SoftwareProcessImpl extends AbstractEntity implements Soft protected MachineLocation getMachineOrNull() { return Iterables.get(Iterables.filter(getLocations(), MachineLocation.class), 0, null); } - + @Override - public void init() { - super.init(); - + protected void initEnrichers() { + super.initEnrichers(); addEnricher(Enrichers.builder().updatingMap(Attributes.SERVICE_NOT_UP_INDICATORS) .from(SERVICE_PROCESS_IS_RUNNING) .computing(Functionals.ifNotEquals(true).value("The software process for this entity does not appear to be running")) .build()); - - addEnricher(ServiceNotUpLogic.newEnricherForServiceUpIfNoNotUpIndicators()); } /** @@ -251,11 +247,13 @@ public abstract class SoftwareProcessImpl extends AbstractEntity implements Soft public void onManagementStarting() { super.onManagementStarting(); - Lifecycle state = getAttribute(SERVICE_STATE); + Lifecycle state = getAttribute(SERVICE_STATE_ACTUAL); if (state == null || state == Lifecycle.CREATED) { // Expect this is a normal start() sequence (i.e. start() will subsequently be called) setAttribute(SERVICE_UP, false); - setAttribute(SERVICE_STATE, Lifecycle.CREATED); + ServiceStateLogic.setExpectedState(this, Lifecycle.CREATED); + // force actual to be created because this is expected subsequently + setAttribute(SERVICE_STATE_ACTUAL, Lifecycle.CREATED); } } @@ -263,7 +261,7 @@ public abstract class SoftwareProcessImpl extends AbstractEntity implements Soft public void onManagementStarted() { super.onManagementStarted(); - Lifecycle state = getAttribute(SERVICE_STATE); + Lifecycle state = getAttribute(SERVICE_STATE_ACTUAL); if (state != null && state != Lifecycle.CREATED) { postRebind(); } @@ -271,7 +269,7 @@ public abstract class SoftwareProcessImpl extends AbstractEntity implements Soft @Override public void rebind() { - Lifecycle state = getAttribute(SERVICE_STATE); + Lifecycle state = getAttribute(SERVICE_STATE_ACTUAL); if (state == null || state != Lifecycle.RUNNING) { log.warn("On rebind of {}, not rebinding because state is {}", this, state); return; @@ -309,11 +307,12 @@ public abstract class SoftwareProcessImpl extends AbstractEntity implements Soft Entities.waitForServiceUp(this, Duration.of(duration, units)); } + /** @deprecated since 0.7.0, this isn't a general test for modifiability, and was hardly ever used (now never used) */ + @Deprecated public void checkModifiable() { - Lifecycle state = getAttribute(SERVICE_STATE); - if (getAttribute(SERVICE_STATE) == Lifecycle.RUNNING) return; - if (getAttribute(SERVICE_STATE) == Lifecycle.STARTING) return; - // TODO this check may be redundant or even inappropriate + Lifecycle state = getAttribute(SERVICE_STATE_ACTUAL); + if (getAttribute(SERVICE_STATE_ACTUAL) == Lifecycle.RUNNING) return; + if (getAttribute(SERVICE_STATE_ACTUAL) == Lifecycle.STARTING) return; throw new IllegalStateException("Cannot configure entity "+this+" in state "+state); } @@ -400,20 +399,21 @@ public abstract class SoftwareProcessImpl extends AbstractEntity implements Soft try { isRunningResult = driver.isRunning(); } catch (Exception e) { - setAttribute(SERVICE_STATE, Lifecycle.ON_FIRE); + ServiceStateLogic.setExpectedState(this, Lifecycle.ON_FIRE); // provide extra context info, as we're seeing this happen in strange circumstances if (driver==null) throw new IllegalStateException(this+" concurrent start and shutdown detected"); throw new IllegalStateException("Error detecting whether "+this+" is running: "+e, e); } if (log.isDebugEnabled()) log.debug("checked {}, is running returned: {}", this, isRunningResult); - // slow exponential delay -- 1.1^N means after 40 tries and 50s elapsed, it reaches the max of 5s intervals + // slow exponential delay -- 1.1^N means after 40 tries and 50s elapsed, it reaches the max of 5s intervals + // TODO use Repeater delay = Math.min(delay*11/10, 5000); } if (!isRunningResult) { String msg = "Software process entity "+this+" did not pass is-running check within "+ "the required "+startTimeout+" limit ("+timer.getDurationElapsed().toStringRounded()+" elapsed)"; log.warn(msg+" (throwing)"); - setAttribute(SERVICE_STATE, Lifecycle.ON_FIRE); + ServiceStateLogic.setExpectedState(this, Lifecycle.RUNNING); throw new IllegalStateException(msg); } } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynEntityMirrorImpl.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynEntityMirrorImpl.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynEntityMirrorImpl.java index 8407708..24c3eeb 100644 --- a/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynEntityMirrorImpl.java +++ b/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynEntityMirrorImpl.java @@ -33,9 +33,7 @@ import brooklyn.entity.basic.Attributes; import brooklyn.entity.basic.BrooklynTaskTags; import brooklyn.entity.basic.Entities; import brooklyn.entity.basic.EntityFunctions; -import brooklyn.entity.basic.Lifecycle; import brooklyn.entity.effector.EffectorBody; -import brooklyn.event.AttributeSensor; import brooklyn.event.basic.Sensors; import brooklyn.event.feed.http.HttpFeed; import brooklyn.event.feed.http.HttpPollConfig; @@ -43,6 +41,7 @@ import brooklyn.util.collections.Jsonya; import brooklyn.util.collections.MutableMap; import brooklyn.util.config.ConfigBag; import brooklyn.util.exceptions.Exceptions; +import brooklyn.util.guava.Functionals; import brooklyn.util.http.HttpTool; import brooklyn.util.http.HttpTool.HttpClientBuilder; import brooklyn.util.http.HttpToolResponse; @@ -53,6 +52,7 @@ import brooklyn.util.task.Tasks; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Preconditions; +import com.google.common.base.Suppliers; import com.google.gson.Gson; public class BrooklynEntityMirrorImpl extends AbstractEntity implements BrooklynEntityMirror { @@ -90,10 +90,9 @@ public class BrooklynEntityMirrorImpl extends AbstractEntity implements Brooklyn .period(getConfig(POLL_PERIOD)) .poll(HttpPollConfig.forMultiple() .onSuccess(mirrorSensors) - .onFailureOrException(EntityFunctions.settingSensorsConstantFunction(this, MutableMap.<AttributeSensor<?>,Object>of( - Attributes.SERVICE_STATE, Lifecycle.ON_FIRE, - MIRROR_STATUS, "error contacting service" - ))) ) + .onFailureOrException(Functionals.function(EntityFunctions.updatingSensorMapEntry(this, Attributes.SERVICE_PROBLEMS, "mirror-feed", + Suppliers.ofInstance("error contacting service") + )))) .build(); } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java b/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java index 3272f3c..b708b61 100644 --- a/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java +++ b/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java @@ -41,6 +41,7 @@ import brooklyn.entity.basic.EffectorStartableImpl.StartParameters; import brooklyn.entity.basic.Entities; import brooklyn.entity.basic.EntityInternal; import brooklyn.entity.basic.Lifecycle; +import brooklyn.entity.basic.ServiceStateLogic; import brooklyn.entity.basic.SoftwareProcess; import brooklyn.entity.effector.EffectorBody; import brooklyn.entity.effector.Effectors; @@ -191,16 +192,15 @@ public abstract class MachineLifecycleEffectorTasks { // --------------------- - /** runs the tasks needed to start, wrapped by setting {@link Attributes#SERVICE_STATE} appropriately */ + /** runs the tasks needed to start, wrapped by setting {@link Attributes#SERVICE_STATE_EXPECTED} appropriately */ public void start(Collection<? extends Location> locations) { - entity().setAttribute(Attributes.SERVICE_STATE, Lifecycle.STARTING); + ServiceStateLogic.setExpectedState(entity(), Lifecycle.STARTING); try { startInLocations(locations); DynamicTasks.waitForLast(); - if (entity().getAttribute(Attributes.SERVICE_STATE) == Lifecycle.STARTING) - entity().setAttribute(Attributes.SERVICE_STATE, Lifecycle.RUNNING); + ServiceStateLogic.setExpectedState(entity(), Lifecycle.RUNNING); } catch (Throwable t) { - entity().setAttribute(Attributes.SERVICE_STATE, Lifecycle.ON_FIRE); + ServiceStateLogic.setExpectedState(entity(), Lifecycle.ON_FIRE); throw Exceptions.propagate(t); } } @@ -298,6 +298,7 @@ public abstract class MachineLifecycleEffectorTasks { entity().setAttribute(Attributes.HOSTNAME, machine.getAddress().getHostName()); entity().setAttribute(Attributes.ADDRESS, machine.getAddress().getHostAddress()); if (machine instanceof SshMachineLocation) { + @SuppressWarnings("resource") SshMachineLocation sshMachine = (SshMachineLocation) machine; UserAndHostAndPort sshAddress = UserAndHostAndPort.fromParts(sshMachine.getUser(), sshMachine.getAddress().getHostName(), sshMachine.getPort()); entity().setAttribute(Attributes.SSH_ADDRESS, sshAddress); @@ -396,7 +397,7 @@ public abstract class MachineLifecycleEffectorTasks { /** default restart impl, stops processes if possible, then starts the entity again */ public void restart() { - entity().setAttribute(Attributes.SERVICE_STATE, Lifecycle.STOPPING); + ServiceStateLogic.setExpectedState(entity(), Lifecycle.STOPPING); DynamicTasks.queue("stopping (process)", new Callable<String>() { public String call() { DynamicTasks.markInessential(); stopProcessesAtMachine(); @@ -407,11 +408,10 @@ public abstract class MachineLifecycleEffectorTasks { DynamicTasks.queue("starting", new Runnable() { public void run() { // startInLocations will look up the location, and provision a machine if necessary // (if it remembered the provisioning location) - entity().setAttribute(Attributes.SERVICE_STATE, Lifecycle.STARTING); + ServiceStateLogic.setExpectedState(entity(), Lifecycle.STARTING); startInLocations(null); DynamicTasks.waitForLast(); - if (entity().getAttribute(Attributes.SERVICE_STATE) == Lifecycle.STARTING) - entity().setAttribute(Attributes.SERVICE_STATE, Lifecycle.RUNNING); + ServiceStateLogic.setExpectedState(entity(), Lifecycle.RUNNING); }}); } @@ -424,17 +424,17 @@ public abstract class MachineLifecycleEffectorTasks { log.info("Stopping {} in {}", entity(), entity().getLocations()); DynamicTasks.queue("pre-stop", new Callable<String>() { public String call() { - if (entity().getAttribute(SoftwareProcess.SERVICE_STATE)==Lifecycle.STOPPED) { + if (entity().getAttribute(SoftwareProcess.SERVICE_STATE_ACTUAL)==Lifecycle.STOPPED) { log.debug("Skipping stop of entity "+entity()+" when already stopped"); return "Already stopped"; } - entity().setAttribute(SoftwareProcess.SERVICE_STATE, Lifecycle.STOPPING); + ServiceStateLogic.setExpectedState(entity(), Lifecycle.STOPPING); entity().setAttribute(SoftwareProcess.SERVICE_UP, false); preStopCustom(); return null; }}); - if (entity().getAttribute(SoftwareProcess.SERVICE_STATE)==Lifecycle.STOPPED) { + if (entity().getAttribute(SoftwareProcess.SERVICE_STATE_ACTUAL)==Lifecycle.STOPPED) { return; } @@ -448,7 +448,7 @@ public abstract class MachineLifecycleEffectorTasks { // Release this machine (even if error trying to stop process - we rethrow that after) Task<StopMachineDetails<Integer>> stoppingMachine = DynamicTasks.queue("stopping (machine)", new Callable<StopMachineDetails<Integer>>() { public StopMachineDetails<Integer> call() { - if (entity().getAttribute(SoftwareProcess.SERVICE_STATE)==Lifecycle.STOPPED) { + if (entity().getAttribute(SoftwareProcess.SERVICE_STATE_ACTUAL)==Lifecycle.STOPPED) { log.debug("Skipping stop of entity "+entity()+" when already stopped"); return new StopMachineDetails<Integer>("Already stopped", 0); } @@ -478,9 +478,9 @@ public abstract class MachineLifecycleEffectorTasks { } entity().setAttribute(SoftwareProcess.SERVICE_UP, false); - entity().setAttribute(SoftwareProcess.SERVICE_STATE, Lifecycle.STOPPED); + ServiceStateLogic.setExpectedState(entity(), Lifecycle.STOPPED); } catch (Throwable e) { - entity().setAttribute(SoftwareProcess.SERVICE_STATE, Lifecycle.ON_FIRE); + ServiceStateLogic.setExpectedState(entity(), Lifecycle.ON_FIRE); Exceptions.propagate(e); } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/base/src/test/java/brooklyn/entity/basic/lifecycle/ScriptHelperTest.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/brooklyn/entity/basic/lifecycle/ScriptHelperTest.java b/software/base/src/test/java/brooklyn/entity/basic/lifecycle/ScriptHelperTest.java index 6edd9a9..daa6109 100644 --- a/software/base/src/test/java/brooklyn/entity/basic/lifecycle/ScriptHelperTest.java +++ b/software/base/src/test/java/brooklyn/entity/basic/lifecycle/ScriptHelperTest.java @@ -117,6 +117,7 @@ public class ScriptHelperTest extends BrooklynAppUnitTestSupport { FunctionFeed.builder() .entity(this) .period(Duration.millis(10)) + .onlyIfServiceUp() .poll(new FunctionPollConfig<Boolean, Boolean>(SERVICE_PROCESS_IS_RUNNING) .onException(Functions.constant(Boolean.FALSE)) .callable(new Callable<Boolean>() { http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97eed6bd/software/base/src/test/java/brooklyn/entity/java/VanillaJavaAppTest.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/brooklyn/entity/java/VanillaJavaAppTest.java b/software/base/src/test/java/brooklyn/entity/java/VanillaJavaAppTest.java index a1347bf..f719882 100644 --- a/software/base/src/test/java/brooklyn/entity/java/VanillaJavaAppTest.java +++ b/software/base/src/test/java/brooklyn/entity/java/VanillaJavaAppTest.java @@ -127,10 +127,10 @@ public class VanillaJavaAppTest { .configure("main", main).configure("classpath", ImmutableList.of(BROOKLYN_THIS_CLASSPATH)) .configure("args", ImmutableList.of())); app.start(ImmutableList.of(loc)); - assertEquals(javaProcess.getAttribute(VanillaJavaApp.SERVICE_STATE), Lifecycle.RUNNING); + assertEquals(javaProcess.getAttribute(VanillaJavaApp.SERVICE_STATE_ACTUAL), Lifecycle.RUNNING); javaProcess.stop(); - assertEquals(javaProcess.getAttribute(VanillaJavaApp.SERVICE_STATE), Lifecycle.STOPPED); + assertEquals(javaProcess.getAttribute(VanillaJavaApp.SERVICE_STATE_ACTUAL), Lifecycle.STOPPED); } @Test(groups={"Integration"})
