http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/d03f254b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogic.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogic.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogic.java deleted file mode 100644 index e64e41e..0000000 --- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogic.java +++ /dev/null @@ -1,639 +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 org.apache.brooklyn.core.entity.lifecycle; - -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.annotation.Nullable; - -import org.apache.brooklyn.api.effector.Effector; -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.entity.EntityLocal; -import org.apache.brooklyn.api.entity.Group; -import org.apache.brooklyn.api.sensor.AttributeSensor; -import org.apache.brooklyn.api.sensor.Enricher; -import org.apache.brooklyn.api.sensor.EnricherSpec; -import org.apache.brooklyn.api.sensor.EnricherSpec.ExtensibleEnricherSpec; -import org.apache.brooklyn.api.sensor.Sensor; -import org.apache.brooklyn.api.sensor.SensorEvent; -import org.apache.brooklyn.api.sensor.SensorEventListener; -import org.apache.brooklyn.config.ConfigInheritance; -import org.apache.brooklyn.config.ConfigKey; -import org.apache.brooklyn.core.BrooklynLogging; -import org.apache.brooklyn.core.BrooklynLogging.LoggingLevel; -import org.apache.brooklyn.core.config.ConfigKeys; -import org.apache.brooklyn.core.enricher.AbstractEnricher; -import org.apache.brooklyn.core.entity.Attributes; -import org.apache.brooklyn.core.entity.Entities; -import org.apache.brooklyn.core.entity.EntityAdjuncts; -import org.apache.brooklyn.core.entity.EntityInternal; -import org.apache.brooklyn.core.entity.EntityPredicates; -import org.apache.brooklyn.core.entity.lifecycle.Lifecycle.Transition; -import org.apache.brooklyn.enricher.stock.AbstractMultipleSensorAggregator; -import org.apache.brooklyn.enricher.stock.Enrichers; -import org.apache.brooklyn.enricher.stock.UpdatingMap; -import org.apache.brooklyn.util.collections.CollectionFunctionals; -import org.apache.brooklyn.util.collections.MutableList; -import org.apache.brooklyn.util.collections.MutableMap; -import org.apache.brooklyn.util.collections.MutableSet; -import org.apache.brooklyn.util.collections.QuorumCheck; -import org.apache.brooklyn.util.core.task.ValueResolver; -import org.apache.brooklyn.util.guava.Functionals; -import org.apache.brooklyn.util.guava.Maybe; -import org.apache.brooklyn.util.repeat.Repeater; -import org.apache.brooklyn.util.text.Strings; -import org.apache.brooklyn.util.time.Duration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Function; -import com.google.common.base.Functions; -import com.google.common.base.Preconditions; -import com.google.common.base.Stopwatch; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.reflect.TypeToken; - -/** Logic, sensors and enrichers, and conveniences, for computing service status */ -public class ServiceStateLogic { - - private static final Logger log = LoggerFactory.getLogger(ServiceStateLogic.class); - - 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<Map<String,Object>> SERVICE_NOT_UP_DIAGNOSTICS = Attributes.SERVICE_NOT_UP_DIAGNOSTICS; - - 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() {} - - public static <TKey,TVal> TVal getMapSensorEntry(EntityLocal entity, AttributeSensor<Map<TKey,TVal>> sensor, TKey key) { - Map<TKey, TVal> map = entity.getAttribute(sensor); - if (map==null) return null; - return map.get(key); - } - - @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, final TKey key, final TVal v) { - /* - * Important to *not* modify the existing attribute value; must make a copy, modify that, and publish. - * This is because a Propagator enricher will set this same value on another entity. There was very - * strange behaviour when this was done for a SERVICE_UP_INDICATORS sensor - the updates done here - * applied to the attribute of both entities! - * - * Need to do this update atomically (i.e. sequentially) because there is no threading control for - * what is calling updateMapSensorEntity. It is called directly on start, on initialising enrichers, - * and in event listeners. These calls could be concurrent. - */ - Function<Map<TKey,TVal>, Maybe<Map<TKey,TVal>>> modifier = new Function<Map<TKey,TVal>, Maybe<Map<TKey,TVal>>>() { - @Override public Maybe<Map<TKey, TVal>> apply(Map<TKey, TVal> map) { - boolean created = (map==null); - if (created) map = MutableMap.of(); - - boolean changed; - if (v == Entities.REMOVE) { - changed = map.containsKey(key); - if (changed) { - map = MutableMap.copyOf(map); - 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 = MutableMap.copyOf(map); - map.put(key, (TVal)v); - } - } - if (changed || created) { - return Maybe.of(map); - } else { - return Maybe.absent(); - } - } - }; - - if (!Entities.isNoLongerManaged(entity)) { - entity.sensors().modify(sensor, modifier); - } - } - - public static void setExpectedState(Entity entity, Lifecycle state) { - if (state==Lifecycle.RUNNING) { - Boolean up = ((EntityInternal)entity).getAttribute(Attributes.SERVICE_UP); - if (!Boolean.TRUE.equals(up) && !Boolean.TRUE.equals(Entities.isReadOnly(entity))) { - // pause briefly to allow any recent problem-clearing processing to complete - Stopwatch timer = Stopwatch.createStarted(); - boolean nowUp = Repeater.create() - .every(ValueResolver.REAL_QUICK_PERIOD) - .limitTimeTo(ValueResolver.PRETTY_QUICK_WAIT) - .until(entity, EntityPredicates.attributeEqualTo(Attributes.SERVICE_UP, true)) - .run(); - if (nowUp) { - log.debug("Had to wait "+Duration.of(timer)+" for "+entity+" "+Attributes.SERVICE_UP+" to be true before setting "+state); - } else { - log.warn("Service is not up when setting "+state+" on "+entity+"; delayed "+Duration.of(timer)+" " - + "but "+Attributes.SERVICE_UP+" did not recover from "+up+"; not-up-indicators="+entity.getAttribute(Attributes.SERVICE_NOT_UP_INDICATORS)); - } - } - } - ((EntityInternal)entity).sensors().set(Attributes.SERVICE_STATE_EXPECTED, new Lifecycle.Transition(state, new Date())); - - Maybe<Enricher> enricher = EntityAdjuncts.tryFindWithUniqueTag(entity.enrichers(), ComputeServiceState.DEFAULT_ENRICHER_UNIQUE_TAG); - if (enricher.isPresent() && enricher.get() instanceof ComputeServiceState) { - ((ComputeServiceState)enricher.get()).onEvent(null); - } - } - 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 { - public static final String DEFAULT_ENRICHER_UNIQUE_TAG = "service.isUp if no service.notUp.indicators"; - - /** static only; not for instantiation */ - private ServiceNotUpLogic() {} - - public static final EnricherSpec<?> newEnricherForServiceUpIfNotUpIndicatorsEmpty() { - return Enrichers.builder() - .transforming(SERVICE_NOT_UP_INDICATORS).<Object>publishing(Attributes.SERVICE_UP) - .suppressDuplicates(true) - .computing( - Functionals.<Map<String,?>> - ifNotEquals(null).<Object>apply(Functions.forPredicate(CollectionFunctionals.<String>mapSizeEquals(0))) - .defaultValue(Entities.REMOVE) ) - .uniqueTag(DEFAULT_ENRICHER_UNIQUE_TAG) - .build(); - } - - /** puts the given value into the {@link Attributes#SERVICE_NOT_UP_INDICATORS} map as if the - * {@link UpdatingMap} enricher for the given key */ - public static void updateNotUpIndicator(EntityLocal entity, String key, Object value) { - updateMapSensorEntry(entity, Attributes.SERVICE_NOT_UP_INDICATORS, key, value); - } - /** clears any entry for the given key in the {@link Attributes#SERVICE_NOT_UP_INDICATORS} map */ - public static void clearNotUpIndicator(EntityLocal entity, String key) { - clearMapSensorEntry(entity, Attributes.SERVICE_NOT_UP_INDICATORS, key); - } - /** as {@link #updateNotUpIndicator(EntityLocal, String, Object)} using the given sensor as the key */ - public static void updateNotUpIndicator(EntityLocal entity, Sensor<?> sensor, Object value) { - updateMapSensorEntry(entity, Attributes.SERVICE_NOT_UP_INDICATORS, sensor.getName(), value); - } - /** as {@link #clearNotUpIndicator(EntityLocal, String)} using the given sensor as the key */ - 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, org.apache.brooklyn.core.entity.lifecycle.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 static final String DEFAULT_ENRICHER_UNIQUE_TAG = "service.state.actual"; - - public ComputeServiceState() {} - public ComputeServiceState(Map<?,?> flags) { super(flags); } - - @Override - public void init() { - super.init(); - if (uniqueTag==null) uniqueTag = DEFAULT_ENRICHER_UNIQUE_TAG; - } - - @Override - public void setEntity(EntityLocal entity) { - super.setEntity(entity); - if (suppressDuplicates==null) { - // only publish on changes, unless it is configured otherwise - suppressDuplicates = true; - } - - subscriptions().subscribe(entity, SERVICE_PROBLEMS, this); - subscriptions().subscribe(entity, SERVICE_UP, this); - subscriptions().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 { - if (!Lifecycle.ON_FIRE.equals(entity.getAttribute(SERVICE_STATE_ACTUAL))) { - BrooklynLogging.log(log, BrooklynLogging.levelDependingIfReadOnly(entity, LoggingLevel.WARN, LoggingLevel.TRACE, LoggingLevel.DEBUG), - "Setting "+entity+" "+Lifecycle.ON_FIRE+" due to problems when expected running, up="+serviceUp+", "+ - (problems==null || problems.isEmpty() ? "not-up-indicators: "+entity.getAttribute(SERVICE_NOT_UP_INDICATORS) : "problems: "+problems)); - } - return Lifecycle.ON_FIRE; - } - } - - protected Lifecycle computeActualStateWhenNotExpectedRunning(Map<String, Object> problems, Boolean up, Lifecycle.Transition stateTransition) { - if (stateTransition!=null) { - // if expected state is present but not running, just echo the expected state (ignore problems and up-ness) - return stateTransition.getState(); - - } else if (problems!=null && !problems.isEmpty()) { - // if there is no expected state, then if service is not up, say stopped, else say on fire (whether service up is true or not present) - if (Boolean.FALSE.equals(up)) { - return Lifecycle.STOPPED; - } else { - BrooklynLogging.log(log, BrooklynLogging.levelDependingIfReadOnly(entity, LoggingLevel.WARN, LoggingLevel.TRACE, LoggingLevel.DEBUG), - "Setting "+entity+" "+Lifecycle.ON_FIRE+" due to problems when expected "+stateTransition+" / up="+up+": "+problems); - return Lifecycle.ON_FIRE; - } - } else { - // no expected transition and no problems - // if the problems map is non-null, then infer from service up; - // if there is no problems map, then leave unchanged (user may have set it explicitly) - if (problems!=null) - return (up==null ? null /* remove if up is not set */ : - up ? Lifecycle.RUNNING : Lifecycle.STOPPED); - else - return entity.getAttribute(SERVICE_STATE_ACTUAL); - } - } - - protected void setActualState(@Nullable Lifecycle state) { - if (log.isTraceEnabled()) log.trace("{} setting actual state {}", this, state); - if (((EntityInternal)entity).getManagementSupport().isNoLongerManaged()) { - // won't catch everything, but catches some - BrooklynLogging.log(log, BrooklynLogging.levelDebugOrTraceIfReadOnly(entity), - entity+" is no longer managed when told to set actual state to "+state+"; suppressing"); - return; - } - 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); - } - - 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()); - } - /** as {@link #updateProblemsIndicator(EntityLocal, Sensor, Object)} */ - public static void updateProblemsIndicator(EntityLocal entity, String key, Object value) { - updateMapSensorEntry(entity, Attributes.SERVICE_PROBLEMS, key, value); - } - /** as {@link #clearProblemsIndicator(EntityLocal, Sensor)} */ - public static void clearProblemsIndicator(EntityLocal entity, String key) { - clearMapSensorEntry(entity, Attributes.SERVICE_PROBLEMS, key); - } - } - - 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 DEFAULT_UNIQUE_TAG = "service-lifecycle-indicators-from-children-and-members"; - - /** as {@link #DEFAULT_UNIQUE_TAG}, but when a second distinct instance is responsible for computing service up */ - public final static String DEFAULT_UNIQUE_TAG_UP = "service-not-up-indicators-from-children-and-members"; - - public static final ConfigKey<QuorumCheck> UP_QUORUM_CHECK = ConfigKeys.builder(QuorumCheck.class, "enricher.service_state.children_and_members.quorum.up") - .description("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") - .defaultValue(QuorumCheck.QuorumChecks.atLeastOneUnlessEmpty()) - .inheritance(ConfigInheritance.NONE) - .build(); - public static final ConfigKey<QuorumCheck> RUNNING_QUORUM_CHECK = ConfigKeys.builder(QuorumCheck.class, "enricher.service_state.children_and_members.quorum.running") - .description("Logic for checking whether this service is healthy, based on children and/or members running, defaulting to requiring none to be ON-FIRE") - .defaultValue(QuorumCheck.QuorumChecks.all()) - .inheritance(ConfigInheritance.NONE) - .build(); - // TODO items below should probably also have inheritance NONE ? - 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); - public static final ConfigKey<Boolean> IGNORE_ENTITIES_WITH_SERVICE_UP_NULL = ConfigKeys.newBooleanConfigKey("enricher.service_state.children_and_members.ignore_entities.service_up_null", "Whether to ignore children reporting null values for service up", true); - @SuppressWarnings("serial") - public static final ConfigKey<Set<Lifecycle>> IGNORE_ENTITIES_WITH_THESE_SERVICE_STATES = ConfigKeys.newConfigKey(new TypeToken<Set<Lifecycle>>() {}, - "enricher.service_state.children_and_members.ignore_entities.service_state_values", - "Service states (including null) which indicate an entity should be ignored when looking at children service states; anything apart from RUNNING not in this list will be treated as not healthy (by default just ON_FIRE will mean not healthy)", - MutableSet.<Lifecycle>builder().addAll(Lifecycle.values()).add(null).remove(Lifecycle.RUNNING).remove(Lifecycle.ON_FIRE).build().asUnmodifiable()); - - protected String getKeyForMapSensor() { - return Preconditions.checkNotNull(super.getUniqueTag()); - } - - @Override - protected void setEntityLoadingConfig() { - fromChildren = true; - fromMembers = true; - // above sets default - super.setEntityLoadingConfig(); - if (isAggregatingMembers() && (!(entity instanceof Group))) { - if (fromChildren) fromMembers=false; - else throw new IllegalStateException("Cannot monitor only members for non-group entity "+entity+": "+this); - } - Preconditions.checkNotNull(getKeyForMapSensor()); - } - - @Override - protected void setEntityLoadingTargetConfig() { - if (getConfig(TARGET_SENSOR)!=null) - throw new IllegalArgumentException("Must not set "+TARGET_SENSOR+" when using "+this); - } - - @Override - public void setEntity(EntityLocal entity) { - super.setEntity(entity); - if (suppressDuplicates==null) { - // only publish on changes, unless it is configured otherwise - suppressDuplicates = true; - } - } - - final static Set<ConfigKey<?>> RECONFIGURABLE_KEYS = ImmutableSet.<ConfigKey<?>>of( - UP_QUORUM_CHECK, RUNNING_QUORUM_CHECK, - DERIVE_SERVICE_NOT_UP, DERIVE_SERVICE_NOT_UP, - IGNORE_ENTITIES_WITH_SERVICE_UP_NULL, IGNORE_ENTITIES_WITH_THESE_SERVICE_STATES); - - @Override - protected <T> void doReconfigureConfig(ConfigKey<T> key, T val) { - if (RECONFIGURABLE_KEYS.contains(key)) { - return; - } else { - super.doReconfigureConfig(key, val); - } - } - - @Override - protected void onChanged() { - super.onChanged(); - if (entity != null && isRunning()) - onUpdated(); - } - - 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() { - if (entity==null || !Entities.isManaged(entity)) { - // either invoked during setup or entity has become unmanaged; just ignore - BrooklynLogging.log(log, BrooklynLogging.levelDebugOrTraceIfReadOnly(entity), - "Ignoring {} onUpdated when entity is not in valid state ({})", this, entity); - return; - } - - // override superclass to publish multiple sensors - 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(); - boolean ignoreNull = getConfig(IGNORE_ENTITIES_WITH_SERVICE_UP_NULL); - Set<Lifecycle> ignoreStates = getConfig(IGNORE_ENTITIES_WITH_THESE_SERVICE_STATES); - int entries=0; - int numUp=0; - for (Map.Entry<Entity, Boolean> state: values.entrySet()) { - if (ignoreNull && state.getValue()==null) - continue; - entries++; - Lifecycle entityState = state.getKey().getAttribute(SERVICE_STATE_ACTUAL); - - if (Boolean.TRUE.equals(state.getValue())) numUp++; - else if (!ignoreStates.contains(entityState)) { - violators.add(state.getKey()); - } - } - - QuorumCheck qc = getConfig(UP_QUORUM_CHECK); - if (qc!=null) { - if (qc.isQuorate(numUp, violators.size()+numUp)) - // quorate - return null; - - if (values.isEmpty()) return "No entities present"; - if (entries==0) return "No entities publishing service up"; - 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()==entries) 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; - List<Entity> onesNotHealthy=MutableList.of(); - Set<Lifecycle> ignoreStates = getConfig(IGNORE_ENTITIES_WITH_THESE_SERVICE_STATES); - for (Map.Entry<Entity,Lifecycle> state: values.entrySet()) { - if (state.getValue()==Lifecycle.RUNNING) numRunning++; - else if (!ignoreStates.contains(state.getValue())) - onesNotHealthy.add(state.getKey()); - } - - QuorumCheck qc = getConfig(RUNNING_QUORUM_CHECK); - if (qc!=null) { - if (qc.isQuorate(numRunning, onesNotHealthy.size()+numRunning)) - // quorate - return null; - - if (onesNotHealthy.isEmpty()) - return "Not enough entities running to be quorate"; - } else { - if (onesNotHealthy.isEmpty()) - return null; - } - - return "Required entit"+Strings.ies(onesNotHealthy.size())+" not healthy: "+ - (onesNotHealthy.size()>3 ? onesNotHealthy.get(0)+" and "+(onesNotHealthy.size()-1)+" others" - : Strings.join(onesNotHealthy, ", ")); - } - - protected void updateMapSensor(AttributeSensor<Map<String, Object>> sensor, Object value) { - if (log.isTraceEnabled()) log.trace("{} updating map sensor {} with {}", new Object[] { this, sensor, 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.enrichers().add(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#DEFAULT_UNIQUE_TAG}), - * 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.DEFAULT_UNIQUE_TAG); - } - - /** as {@link #newEnricherFromChildren()} but only publishing service not-up indicators, - * using a different unique tag ({@link ComputeServiceIndicatorsFromChildrenAndMembers#DEFAULT_UNIQUE_TAG_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.DEFAULT_UNIQUE_TAG_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/brooklyn-server/blob/d03f254b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/Changeable.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/Changeable.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/Changeable.java deleted file mode 100644 index 388eb17..0000000 --- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/Changeable.java +++ /dev/null @@ -1,35 +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 org.apache.brooklyn.core.entity.trait; - -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.sensor.AttributeSensor; -import org.apache.brooklyn.core.sensor.BasicNotificationSensor; -import org.apache.brooklyn.core.sensor.Sensors; - -/** - * A collection of entities that can change. - */ -public interface Changeable { - - AttributeSensor<Integer> GROUP_SIZE = Sensors.newIntegerSensor("group.members.count", "Number of members"); - - BasicNotificationSensor<Entity> MEMBER_ADDED = new BasicNotificationSensor<Entity>(Entity.class, "group.members.added", "Entity added to group members"); - BasicNotificationSensor<Entity> MEMBER_REMOVED = new BasicNotificationSensor<Entity>(Entity.class, "group.members.removed", "Entity removed from group members"); -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/d03f254b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/MemberReplaceable.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/MemberReplaceable.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/MemberReplaceable.java deleted file mode 100644 index 238e261..0000000 --- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/MemberReplaceable.java +++ /dev/null @@ -1,45 +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 org.apache.brooklyn.core.entity.trait; - -import java.util.NoSuchElementException; - -import org.apache.brooklyn.core.annotation.Effector; -import org.apache.brooklyn.core.annotation.EffectorParam; -import org.apache.brooklyn.core.effector.MethodEffector; -import org.apache.brooklyn.entity.group.StopFailedRuntimeException; - -public interface MemberReplaceable { - - MethodEffector<String> REPLACE_MEMBER = new MethodEffector<String>(MemberReplaceable.class, "replaceMember"); - - /** - * Replaces the entity with the given ID, if it is a member. - * <p> - * First adds a new member, then removes this one. - * - * @param memberId entity id of a member to be replaced - * @return the id of the new entity - * @throws NoSuchElementException If entity cannot be resolved, or it is not a member - * @throws StopFailedRuntimeException If stop failed, after successfully starting replacement - */ - @Effector(description="Replaces the entity with the given ID, if it is a member; first adds a new member, then removes this one. "+ - "Returns id of the new entity; or throws exception if couldn't be replaced.") - String replaceMember(@EffectorParam(name="memberId", description="The entity id of a member to be replaced") String memberId); -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/d03f254b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/Resizable.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/Resizable.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/Resizable.java deleted file mode 100644 index 36e6ba8..0000000 --- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/Resizable.java +++ /dev/null @@ -1,68 +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 org.apache.brooklyn.core.entity.trait; - - -import org.apache.brooklyn.core.annotation.Effector; -import org.apache.brooklyn.core.annotation.EffectorParam; -import org.apache.brooklyn.core.effector.MethodEffector; - -/** - * Defines an entity group that can be re-sized dynamically. - * <p/> - * By invoking the {@link #resize(Integer)} effector, the number of child nodes - * can be reduced (by shutting down some of them) or increased (by provisioning new entities.) - */ -public interface Resizable { - - /** - * Indicates that resizing up to the desired size is not possible - only resized to the - * {@link Resizable#getCurrentSize()}, because there is insufficient capacity. - */ - public static class InsufficientCapacityException extends RuntimeException { - private static final long serialVersionUID = 953230498564942446L; - - public InsufficientCapacityException(String msg) { - super(msg); - } - public InsufficientCapacityException(String msg, Throwable cause) { - super(msg, cause); - } - } - - MethodEffector<Integer> RESIZE = new MethodEffector<Integer>(Resizable.class, "resize"); - - /** - * Grow or shrink this entity to the desired size. - * - * @param desiredSize the new size of the entity group. - * @return the new size of the group. - * - * @throws InsufficientCapacityException If the request was to grow, but there is no capacity to grow to - * the desired size. - */ - @Effector(description="Changes the size of the entity (e.g. the number of nodes in a cluster)") - Integer resize(@EffectorParam(name="desiredSize", description="The new size of the cluster") Integer desiredSize); - - /** - * @return the current size of the group. - */ - Integer getCurrentSize(); -} - http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/d03f254b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/Startable.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/Startable.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/Startable.java deleted file mode 100644 index 96812ce..0000000 --- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/Startable.java +++ /dev/null @@ -1,123 +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 org.apache.brooklyn.core.entity.trait; - -import java.util.Collection; - -import org.apache.brooklyn.api.effector.Effector; -import org.apache.brooklyn.api.location.Location; -import org.apache.brooklyn.api.sensor.AttributeSensor; -import org.apache.brooklyn.config.ConfigKey; -import org.apache.brooklyn.core.annotation.EffectorParam; -import org.apache.brooklyn.core.config.ConfigKeys; -import org.apache.brooklyn.core.effector.EffectorBody; -import org.apache.brooklyn.core.effector.Effectors; -import org.apache.brooklyn.core.effector.MethodEffector; -import org.apache.brooklyn.core.entity.Attributes; -import org.apache.brooklyn.util.core.config.ConfigBag; -import org.apache.brooklyn.util.core.task.Tasks; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This interface describes an {@link org.apache.brooklyn.api.entity.Entity} that can be started and stopped. - * - * The {@link Effector}s are {@link #START}, {@link #STOP} and {@link #RESTART}. The start effector takes - * a collection of {@link Location} objects as an argument which will cause the entity to be started or stopped in all - * these locations. The other effectors will stop or restart the entity in the location(s) it is already running in. - */ -public interface Startable { - - - AttributeSensor<Boolean> SERVICE_UP = Attributes.SERVICE_UP; - - public static class StartEffectorBody extends EffectorBody<Void> { - public static final ConfigKey<Object> LOCATIONS = ConfigKeys.newConfigKey(Object.class, "locations", - "The location or locations to start in, as a string, a location object, a list of strings, " - + "or a list of location objects"); - @Override public Void call(ConfigBag parameters) { - parameters.put(LOCATIONS, entity().getManagementContext().getLocationRegistry().resolveList(parameters.get(LOCATIONS))); - return new MethodEffector<Void>(Startable.class, "start").call(entity(), parameters.getAllConfig()); - } - } - - public static class StopEffectorBody extends EffectorBody<Void> { - private static final Logger log = LoggerFactory.getLogger(Startable.class); - - @Override public Void call(ConfigBag parameters) { - if (!parameters.isEmpty()) { - log.warn("Parameters "+parameters+" not supported for call to "+entity()+" - "+Tasks.current()); - } - - return new MethodEffector<Void>(Startable.class, "stop").call(entity(), parameters.getAllConfig()); - } - } - - public static class RestartEffectorBody extends EffectorBody<Void> { - private static final Logger log = LoggerFactory.getLogger(Startable.class); - - @Override public Void call(ConfigBag parameters) { - if (!parameters.isEmpty()) { - log.warn("Parameters "+parameters+" not supported for call to "+entity()+" - "+Tasks.current()); - } - return new MethodEffector<Void>(Startable.class, "restart").call(entity(), parameters.getAllConfig()); - } - } - - org.apache.brooklyn.api.effector.Effector<Void> START = Effectors.effector(new MethodEffector<Void>(Startable.class, "start")) - // override start to take strings etc - .parameter(StartEffectorBody.LOCATIONS) - .impl(new StartEffectorBody()) - .build(); - - org.apache.brooklyn.api.effector.Effector<Void> STOP = Effectors.effector(new MethodEffector<Void>(Startable.class, "stop")) - .impl(new StopEffectorBody()) - .build(); - - org.apache.brooklyn.api.effector.Effector<Void> RESTART = Effectors.effector(new MethodEffector<Void>(Startable.class, "restart")) - .impl(new RestartEffectorBody()) - .build(); - - /** - * Start the entity in the given collection of locations. - * <p> - * Some entities may define custom {@link Effector} implementations which support - * a richer set of parameters. See the entity-specific {@link #START} effector declaration. - */ - @org.apache.brooklyn.core.annotation.Effector(description="Start the process/service represented by an entity") - void start(@EffectorParam(name="locations") Collection<? extends Location> locations); - - /** - * Stop the entity. - * <p> - * Some entities may define custom {@link Effector} implementations which support - * a richer set of parameters. See the entity-specific {@link #STOP} effector declaration. - */ - @org.apache.brooklyn.core.annotation.Effector(description="Stop the process/service represented by an entity") - void stop(); - - /** - * Restart the entity. - * <p> - * Some entities may define custom {@link Effector} implementations which support - * a richer set of parameters. See the entity-specific {@link #RESTART} effector declaration. - */ - @org.apache.brooklyn.core.annotation.Effector(description="Restart the process/service represented by an entity") - void restart(); -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/d03f254b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/StartableMethods.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/StartableMethods.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/StartableMethods.java deleted file mode 100644 index b49d259..0000000 --- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/StartableMethods.java +++ /dev/null @@ -1,125 +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 org.apache.brooklyn.core.entity.trait; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.entity.EntityLocal; -import org.apache.brooklyn.api.location.Location; -import org.apache.brooklyn.api.mgmt.TaskAdaptable; -import org.apache.brooklyn.core.effector.Effectors; -import org.apache.brooklyn.core.entity.Entities; -import org.apache.brooklyn.core.entity.EntityPredicates; -import org.apache.brooklyn.util.collections.MutableMap; -import org.apache.brooklyn.util.core.config.ConfigBag; -import org.apache.brooklyn.util.core.task.DynamicTasks; -import org.apache.brooklyn.util.core.task.TaskTags; -import org.apache.brooklyn.util.exceptions.CompoundRuntimeException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Predicates; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; - -public class StartableMethods { - - public static final Logger log = LoggerFactory.getLogger(StartableMethods.class); - - private StartableMethods() {} - - /** Common implementation for start in parent nodes; just invokes start on all children of the entity */ - public static void start(EntityLocal e, Collection<? extends Location> locations) { - log.debug("Starting entity "+e+" at "+locations); - DynamicTasks.queueIfPossible(startingChildren(e, locations)).orSubmitAsync(e).getTask().getUnchecked(); - } - - /** Common implementation for stop in parent nodes; just invokes stop on all children of the entity */ - public static void stop(EntityLocal e) { - log.debug("Stopping entity "+e); - DynamicTasks.queueIfPossible(stoppingChildren(e)).orSubmitAsync(e).getTask().getUnchecked(); - if (log.isDebugEnabled()) log.debug("Stopped entity "+e); - } - - /** Common implementation for restart in parent nodes; just invokes restart on all children of the entity */ - public static void restart(EntityLocal e) { - log.debug("Restarting entity "+e); - DynamicTasks.queueIfPossible(restartingChildren(e)).orSubmitAsync(e).getTask().getUnchecked(); - if (log.isDebugEnabled()) log.debug("Restarted entity "+e); - } - - private static <T extends Entity> Iterable<T> filterStartableManagedEntities(Iterable<T> contenders) { - return Iterables.filter(contenders, Predicates.and(Predicates.instanceOf(Startable.class), EntityPredicates.isManaged())); - } - - public static void stopSequentially(Iterable<? extends Startable> entities) { - List<Exception> exceptions = Lists.newArrayList(); - List<Startable> failedEntities = Lists.newArrayList(); - - for (final Startable entity : entities) { - if (!Entities.isManaged((Entity)entity)) { - log.debug("Not stopping {} because it is not managed; continuing", entity); - continue; - } - try { - TaskAdaptable<Void> task = TaskTags.markInessential(Effectors.invocation((Entity)entity, Startable.STOP, Collections.emptyMap())); - DynamicTasks.submit(task, (Entity)entity).getUnchecked(); - } catch (Exception e) { - log.warn("Error stopping "+entity+"; continuing with shutdown", e); - exceptions.add(e); - failedEntities.add(entity); - } - } - - if (exceptions.size() > 0) { - throw new CompoundRuntimeException("Error stopping "+(failedEntities.size() > 1 ? "entities" : "entity")+": "+failedEntities, exceptions); - } - } - - /** unsubmitted task for starting children of the given entity at the same location as the entity */ - public static TaskAdaptable<?> startingChildren(Entity entity) { - return startingChildren(entity, entity.getLocations()); - } - /** unsubmitted task for starting children of the given entity at the given location */ - public static TaskAdaptable<?> startingChildren(Entity entity, Location location) { - return startingChildren(entity, Collections.singleton(location)); - } - /** unsubmitted task for starting children of the given entity at the given locations */ - public static TaskAdaptable<?> startingChildren(Entity entity, Iterable<? extends Location> locations) { - return Effectors.invocation(Startable.START, MutableMap.of("locations", locations), filterStartableManagedEntities(entity.getChildren())); - } - - /** unsubmitted task for stopping children of the given entity */ - public static TaskAdaptable<?> stoppingChildren(Entity entity) { - return Effectors.invocation(Startable.STOP, Collections.emptyMap(), filterStartableManagedEntities(entity.getChildren())); - } - - /** unsubmitted task for restarting children of the given entity */ - public static TaskAdaptable<?> restartingChildren(Entity entity, ConfigBag parameters) { - return Effectors.invocation(Startable.RESTART, parameters.getAllConfig(), filterStartableManagedEntities(entity.getChildren())); - } - /** as {@link #restartingChildren(Entity, ConfigBag)} with no parameters */ - public static TaskAdaptable<?> restartingChildren(Entity entity) { - return restartingChildren(entity, ConfigBag.EMPTY); - } - -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/d03f254b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/feed/AbstractFeed.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/feed/AbstractFeed.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/feed/AbstractFeed.java deleted file mode 100644 index 5b057dd..0000000 --- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/feed/AbstractFeed.java +++ /dev/null @@ -1,246 +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 org.apache.brooklyn.core.feed; - -import static com.google.common.base.Preconditions.checkNotNull; - -import java.util.Collection; - -import org.apache.brooklyn.api.entity.EntityLocal; -import org.apache.brooklyn.api.mgmt.rebind.RebindSupport; -import org.apache.brooklyn.api.mgmt.rebind.mementos.FeedMemento; -import org.apache.brooklyn.api.sensor.Feed; -import org.apache.brooklyn.config.ConfigKey; -import org.apache.brooklyn.core.BrooklynFeatureEnablement; -import org.apache.brooklyn.core.config.ConfigKeys; -import org.apache.brooklyn.core.entity.EntityInternal; -import org.apache.brooklyn.core.mgmt.rebind.BasicFeedRebindSupport; -import org.apache.brooklyn.core.objs.AbstractEntityAdjunct; -import org.apache.brooklyn.util.javalang.JavaClassNames; -import org.apache.brooklyn.util.text.Strings; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Captures common fields and processes for sensor feeds. - * These generally poll or subscribe to get sensor values for an entity. - * They make it easy to poll over http, jmx, etc. - */ -public abstract class AbstractFeed extends AbstractEntityAdjunct implements Feed { - - private static final Logger log = LoggerFactory.getLogger(AbstractFeed.class); - - public static final ConfigKey<Boolean> ONLY_IF_SERVICE_UP = ConfigKeys.newBooleanConfigKey("feed.onlyIfServiceUp", "", false); - - private final Object pollerStateMutex = new Object(); - private transient volatile Poller<?> poller; - private transient volatile boolean activated; - private transient volatile boolean suspended; - - public AbstractFeed() { - } - - /** - * @deprecated since 0.7.0; use no-arg constructor; call {@link #setEntity(EntityLocal)} - */ - @Deprecated - public AbstractFeed(EntityLocal entity) { - this(entity, false); - } - - /** - * @deprecated since 0.7.0; use no-arg constructor; call {@link #setEntity(EntityLocal)} and {@code setConfig(ONLY_IF_SERVICE_UP, onlyIfServiceUp)} - */ - @Deprecated - public AbstractFeed(EntityLocal entity, boolean onlyIfServiceUp) { - this.entity = checkNotNull(entity, "entity"); - setConfig(ONLY_IF_SERVICE_UP, onlyIfServiceUp); - } - - // Ensure idempotent, as called in builders (in case not registered with entity), and also called - // when registering with entity - @Override - public void setEntity(EntityLocal entity) { - super.setEntity(entity); - if (BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_FEED_REGISTRATION_PROPERTY)) { - ((EntityInternal)entity).feeds().addFeed(this); - } - } - - protected void initUniqueTag(String uniqueTag, Object ...valsForDefault) { - if (Strings.isNonBlank(uniqueTag)) this.uniqueTag = uniqueTag; - else this.uniqueTag = getDefaultUniqueTag(valsForDefault); - } - - protected String getDefaultUniqueTag(Object ...valsForDefault) { - StringBuilder sb = new StringBuilder(); - sb.append(JavaClassNames.simpleClassName(this)); - if (valsForDefault.length==0) { - sb.append("@"); - sb.append(hashCode()); - } else if (valsForDefault.length==1 && valsForDefault[0] instanceof Collection){ - sb.append(Strings.toUniqueString(valsForDefault[0], 80)); - } else { - sb.append("["); - boolean first = true; - for (Object x: valsForDefault) { - if (!first) sb.append(";"); - else first = false; - sb.append(Strings.toUniqueString(x, 80)); - } - sb.append("]"); - } - return sb.toString(); - } - - @Override - public void start() { - if (log.isDebugEnabled()) log.debug("Starting feed {} for {}", this, entity); - if (activated) { - throw new IllegalStateException(String.format("Attempt to start feed %s of entity %s when already running", - this, entity)); - } - if (poller != null) { - throw new IllegalStateException(String.format("Attempt to re-start feed %s of entity %s", this, entity)); - } - - poller = new Poller<Object>(entity, getConfig(ONLY_IF_SERVICE_UP)); - activated = true; - preStart(); - synchronized (pollerStateMutex) { - // don't start poller if we are suspended - if (!suspended) { - poller.start(); - } - } - } - - @Override - public void suspend() { - synchronized (pollerStateMutex) { - if (activated && !suspended) { - poller.stop(); - } - suspended = true; - } - } - - @Override - public void resume() { - synchronized (pollerStateMutex) { - if (activated && suspended) { - poller.start(); - } - suspended = false; - } - } - - @Override - public void destroy() { - stop(); - } - - @Override - public void stop() { - if (!activated) { - log.debug("Ignoring attempt to stop feed {} of entity {} when not running", this, entity); - return; - } - if (log.isDebugEnabled()) log.debug("stopping feed {} for {}", this, entity); - - activated = false; - preStop(); - synchronized (pollerStateMutex) { - if (!suspended) { - poller.stop(); - } - } - postStop(); - super.destroy(); - } - - @Override - public boolean isActivated() { - return activated; - } - - public EntityLocal getEntity() { - return entity; - } - - protected boolean isConnected() { - // TODO Default impl will result in multiple logs for same error if becomes unreachable - // (e.g. if ssh gets NoRouteToHostException, then every AttributePollHandler for that - // feed will log.warn - so if polling for 10 sensors/attributes will get 10 log messages). - // Would be nice if reduced this logging duplication. - // (You can reduce it by providing a better 'isConnected' implementation of course.) - return isRunning() && entity!=null && !((EntityInternal)entity).getManagementSupport().isNoLongerManaged(); - } - - @Override - public boolean isSuspended() { - return suspended; - } - - @Override - public boolean isRunning() { - return isActivated() && !isSuspended() && !isDestroyed() && getPoller()!=null && getPoller().isRunning(); - } - - @Override - public RebindSupport<FeedMemento> getRebindSupport() { - return new BasicFeedRebindSupport(this); - } - - @SuppressWarnings("unchecked") - @Override - public RelationSupportInternal<Feed> relations() { - return (RelationSupportInternal<Feed>) super.relations(); - } - - @Override - protected void onChanged() { - // TODO Auto-generated method stub - } - - /** - * For overriding. - */ - protected void preStart() { - } - - /** - * For overriding. - */ - protected void preStop() { - } - - /** - * For overriding. - */ - protected void postStop() { - } - - /** - * For overriding, where sub-class can change return-type generics! - */ - protected Poller<?> getPoller() { - return poller; - } -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/d03f254b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/feed/AttributePollHandler.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/feed/AttributePollHandler.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/feed/AttributePollHandler.java deleted file mode 100644 index a82f5d9..0000000 --- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/feed/AttributePollHandler.java +++ /dev/null @@ -1,248 +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 org.apache.brooklyn.core.feed; - -import static com.google.common.base.Preconditions.checkNotNull; - -import org.apache.brooklyn.api.entity.EntityLocal; -import org.apache.brooklyn.api.sensor.AttributeSensor; -import org.apache.brooklyn.core.entity.Attributes; -import org.apache.brooklyn.core.entity.Entities; -import org.apache.brooklyn.core.entity.EntityInternal; -import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; -import org.apache.brooklyn.core.entity.lifecycle.Lifecycle.Transition; -import org.apache.brooklyn.util.core.flags.TypeCoercions; -import org.apache.brooklyn.util.core.task.Tasks; -import org.apache.brooklyn.util.time.Duration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Objects; - -/** - * Handler for when polling an entity's attribute. On each poll result the entity's attribute is set. - * - * Calls to onSuccess and onError will happen sequentially, but may be called from different threads - * each time. Note that no guarantees of a synchronized block exist, so additional synchronization - * would be required for the Java memory model's "happens before" relationship. - * - * @author aled - */ -public class AttributePollHandler<V> implements PollHandler<V> { - - public static final Logger log = LoggerFactory.getLogger(AttributePollHandler.class); - - private final FeedConfig<V,?,?> config; - private final EntityLocal entity; - @SuppressWarnings("rawtypes") - private final AttributeSensor sensor; - private final AbstractFeed feed; - private final boolean suppressDuplicates; - - // allow 30 seconds before logging at WARN, if there has been no success yet; - // after success WARN immediately - // TODO these should both be configurable - private Duration logWarningGraceTimeOnStartup = Duration.THIRTY_SECONDS; - private Duration logWarningGraceTime = Duration.millis(0); - - // internal state to look after whether to log warnings - private volatile Long lastSuccessTime = null; - private volatile Long currentProblemStartTime = null; - private volatile boolean currentProblemLoggedAsWarning = false; - private volatile boolean lastWasProblem = false; - - - public AttributePollHandler(FeedConfig<V,?,?> config, EntityLocal entity, AbstractFeed feed) { - this.config = checkNotNull(config, "config"); - this.entity = checkNotNull(entity, "entity"); - this.sensor = checkNotNull(config.getSensor(), "sensor"); - this.feed = checkNotNull(feed, "feed"); - this.suppressDuplicates = config.getSupressDuplicates(); - } - - @Override - public boolean checkSuccess(V val) { - // Always true if no checkSuccess predicate was configured. - return !config.hasCheckSuccessHandler() || config.getCheckSuccess().apply(val); - } - - @Override - public void onSuccess(V val) { - if (lastWasProblem) { - if (currentProblemLoggedAsWarning) { - log.info("Success (following previous problem) reading "+getBriefDescription()); - } else { - log.debug("Success (following previous problem) reading "+getBriefDescription()); - } - lastWasProblem = false; - currentProblemStartTime = null; - currentProblemLoggedAsWarning = false; - } - lastSuccessTime = System.currentTimeMillis(); - if (log.isTraceEnabled()) log.trace("poll for {} got: {}", new Object[] {getBriefDescription(), val}); - - try { - setSensor(transformValueOnSuccess(val)); - } catch (Exception e) { - if (feed.isConnected()) { - log.warn("unable to compute "+getBriefDescription()+"; on val="+val, e); - } else { - if (log.isDebugEnabled()) log.debug("unable to compute "+getBriefDescription()+"; val="+val+" (when inactive)", e); - } - } - } - - /** allows post-processing, such as applying a success handler; - * default applies the onSuccess handler (which is recommended) */ - protected Object transformValueOnSuccess(V val) { - return config.hasSuccessHandler() ? config.getOnSuccess().apply(val) : val; - } - - @Override - public void onFailure(V val) { - if (!config.hasFailureHandler()) { - onException(new Exception("checkSuccess of "+this+" for "+getBriefDescription()+" was false but poller has no failure handler")); - } else { - logProblem("failure", val); - - try { - setSensor(config.hasFailureHandler() ? config.getOnFailure().apply((V)val) : val); - } catch (Exception e) { - if (feed.isConnected()) { - log.warn("Error computing " + getBriefDescription() + "; val=" + val+": "+ e, e); - } else { - if (log.isDebugEnabled()) - log.debug("Error computing " + getBriefDescription() + "; val=" + val + " (when inactive)", e); - } - } - } - } - - @Override - public void onException(Exception exception) { - if (!feed.isConnected()) { - if (log.isTraceEnabled()) log.trace("Read of {} in {} gave exception (while not connected or not yet connected): {}", new Object[] {this, getBriefDescription(), exception}); - } else { - logProblem("exception", exception); - } - - if (config.hasExceptionHandler()) { - try { - setSensor( config.getOnException().apply(exception) ); - } catch (Exception e) { - if (feed.isConnected()) { - log.warn("unable to compute "+getBriefDescription()+"; on exception="+exception, e); - } else { - if (log.isDebugEnabled()) log.debug("unable to compute "+getBriefDescription()+"; exception="+exception+" (when inactive)", e); - } - } - } - } - - protected void logProblem(String type, Object val) { - if (lastWasProblem && currentProblemLoggedAsWarning) { - if (log.isTraceEnabled()) - log.trace("Recurring {} reading {} in {}: {}", new Object[] {type, this, getBriefDescription(), val}); - } else { - long nowTime = System.currentTimeMillis(); - // get a non-volatile value - Long currentProblemStartTimeCache = currentProblemStartTime; - long expiryTime = - (lastSuccessTime!=null && !isTransitioningOrStopped()) ? lastSuccessTime+logWarningGraceTime.toMilliseconds() : - currentProblemStartTimeCache!=null ? currentProblemStartTimeCache+logWarningGraceTimeOnStartup.toMilliseconds() : - nowTime+logWarningGraceTimeOnStartup.toMilliseconds(); - if (!lastWasProblem) { - if (expiryTime <= nowTime) { - currentProblemLoggedAsWarning = true; - if (entity==null || !Entities.isNoLongerManaged(entity)) { - log.warn("Read of " + getBriefDescription() + " gave " + type + ": " + val); - } else { - log.debug("Read of " + getBriefDescription() + " gave " + type + ": " + val); - } - if (log.isDebugEnabled() && val instanceof Throwable) - log.debug("Trace for "+type+" reading "+getBriefDescription()+": "+val, (Throwable)val); - } else { - if (log.isDebugEnabled()) - log.debug("Read of " + getBriefDescription() + " gave " + type + " (in grace period): " + val); - } - lastWasProblem = true; - currentProblemStartTime = nowTime; - } else { - if (expiryTime <= nowTime) { - currentProblemLoggedAsWarning = true; - log.warn("Read of " + getBriefDescription() + " gave " + type + - " (grace period expired, occurring for "+Duration.millis(nowTime - currentProblemStartTimeCache)+ - (config.hasExceptionHandler() ? "" : ", no exception handler set for sensor")+ - ")"+ - ": " + val); - if (log.isDebugEnabled() && val instanceof Throwable) - log.debug("Trace for "+type+" reading "+getBriefDescription()+": "+val, (Throwable)val); - } else { - if (log.isDebugEnabled()) - log.debug("Recurring {} reading {} in {} (still in grace period): {}", new Object[] {type, this, getBriefDescription(), val}); - } - } - } - } - - protected boolean isTransitioningOrStopped() { - if (entity==null) return false; - Transition expected = entity.getAttribute(Attributes.SERVICE_STATE_EXPECTED); - if (expected==null) return false; - return (expected.getState()==Lifecycle.STARTING || expected.getState()==Lifecycle.STOPPING || expected.getState()==Lifecycle.STOPPED); - } - - @SuppressWarnings("unchecked") - protected void setSensor(Object v) { - if (Entities.isNoLongerManaged(entity)) { - if (Tasks.isInterrupted()) return; - log.warn(""+entity+" is not managed; feed "+this+" setting "+sensor+" to "+v+" at this time is not supported ("+Tasks.current()+")"); - } - - if (v == FeedConfig.UNCHANGED) { - // nothing - } else if (v == FeedConfig.REMOVE) { - ((EntityInternal)entity).removeAttribute(sensor); - } else if (sensor == FeedConfig.NO_SENSOR) { - // nothing - } else { - Object coercedV = TypeCoercions.coerce(v, sensor.getType()); - if (suppressDuplicates && Objects.equal(coercedV, entity.getAttribute(sensor))) { - // no change; nothing - } else { - entity.sensors().set(sensor, coercedV); - } - } - } - - @Override - public String toString() { - return super.toString()+"["+getDescription()+"]"; - } - - @Override - public String getDescription() { - return sensor.getName()+" @ "+entity.getId()+" <- "+config; - } - - protected String getBriefDescription() { - return ""+entity+"->"+(sensor==FeedConfig.NO_SENSOR ? "(dynamic sensors)" : ""+sensor); - } - -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/d03f254b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/feed/ConfigToAttributes.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/feed/ConfigToAttributes.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/feed/ConfigToAttributes.java deleted file mode 100644 index dc81d2a..0000000 --- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/feed/ConfigToAttributes.java +++ /dev/null @@ -1,59 +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 org.apache.brooklyn.core.feed; - -import org.apache.brooklyn.api.entity.EntityLocal; -import org.apache.brooklyn.api.mgmt.ManagementContext; -import org.apache.brooklyn.api.sensor.Sensor; -import org.apache.brooklyn.core.sensor.AttributeSensorAndConfigKey; -import org.apache.brooklyn.core.sensor.TemplatedStringAttributeSensorAndConfigKey; - - -/** Simple config adapter for setting {@link AttributeSensorAndConfigKey} sensor values from the config value or config default */ -public class ConfigToAttributes { - - //normally just applied once, statically, not registered... - public static void apply(EntityLocal entity) { - for (Sensor<?> it : entity.getEntityType().getSensors()) { - if (it instanceof AttributeSensorAndConfigKey) { - apply(entity, (AttributeSensorAndConfigKey<?,?>)it); - } - } - } - - /** - * Convenience for ensuring an individual sensor is set from its config key - * (e.g. sub-classes of DynamicWebAppCluster that don't want to set HTTP_PORT etc!) - */ - public static <T> T apply(EntityLocal entity, AttributeSensorAndConfigKey<?,T> key) { - T v = entity.getAttribute(key); - if (v!=null) return v; - v = key.getAsSensorValue(entity); - if (v!=null) entity.sensors().set(key, v); - return v; - } - - /** - * Convenience for transforming a config value (e.g. processing a {@link TemplatedStringAttributeSensorAndConfigKey}), - * outside of the context of an entity. - */ - public static <T> T transform(ManagementContext managementContext, AttributeSensorAndConfigKey<?,T> key) { - return key.getAsSensorValue(managementContext); - } -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/d03f254b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/feed/DelegatingPollHandler.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/feed/DelegatingPollHandler.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/feed/DelegatingPollHandler.java deleted file mode 100644 index fae7dd6..0000000 --- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/feed/DelegatingPollHandler.java +++ /dev/null @@ -1,96 +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 org.apache.brooklyn.core.feed; - -import java.util.List; - -import com.google.common.collect.ImmutableList; - -/** - * A poll handler that delegates each call to a set of poll handlers. - * - * @author aled - */ -public class DelegatingPollHandler<V> implements PollHandler<V> { - - private final List<AttributePollHandler<? super V>> delegates; - - public DelegatingPollHandler(Iterable<AttributePollHandler<? super V>> delegates) { - super(); - this.delegates = ImmutableList.copyOf(delegates); - } - - @Override - public boolean checkSuccess(V val) { - for (AttributePollHandler<? super V> delegate : delegates) { - if (!delegate.checkSuccess(val)) - return false; - } - return true; - } - - @Override - public void onSuccess(V val) { - for (AttributePollHandler<? super V> delegate : delegates) { - delegate.onSuccess(val); - } - } - - @Override - public void onFailure(V val) { - for (AttributePollHandler<? super V> delegate : delegates) { - delegate.onFailure(val); - } - } - - @Override - public void onException(Exception exception) { - for (AttributePollHandler<? super V> delegate : delegates) { - delegate.onException(exception); - } - } - - @Override - public String toString() { - return super.toString()+"["+getDescription()+"]"; - } - - @Override - public String getDescription() { - if (delegates.isEmpty()) - return "(empty delegate list)"; - if (delegates.size()==1) - return delegates.get(0).getDescription(); - StringBuilder sb = new StringBuilder(); - sb.append("["); - int count = 0; - for (AttributePollHandler<? super V> delegate : delegates) { - if (count>0) sb.append("; "); - sb.append(delegate.getDescription()); - if (count>2) { - sb.append("; ..."); - break; - } - count++; - } - sb.append("]"); - return sb.toString(); - } - -}
