blob: 8ef52cf8de1b32ca69f16fac4353b20bc678736a [file] [log] [blame]
/*
* 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.sensor.AttributeSensor;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.sensor.Sensors;
import org.apache.brooklyn.feed.http.HttpPollConfig;
import org.apache.brooklyn.util.collections.MutableList;
import org.apache.brooklyn.util.guava.Functionals;
import org.apache.brooklyn.util.javalang.JavaClassNames;
import org.apache.brooklyn.util.text.Strings;
import org.apache.brooklyn.util.time.Duration;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
/**
* Configuration for a poll, or a subscription etc, that is being added to a feed.
*
* @author aled
*/
public class FeedConfig<V, T, F extends FeedConfig<V, T, F>> {
/** The onSuccess or onError functions can return this value to indicate that the sensor should not change. */
public static final Object UNCHANGED = Entities.UNCHANGED;
/** The onSuccess or onError functions can return this value to indicate that the sensor value should be removed
* (cf 'null', but useful in dynamic situations) */
public static final Object REMOVE = Entities.REMOVE;
/** Indicates that no sensor is being used here. This sensor is suppressed,
* but is useful where you want to use the feeds with custom success/exception/failure functions
* which directly set multiple sensors, e.g. dynamically based on the poll response.
* <p>
* See {@link HttpPollConfig#forMultiple()} and its usages.
* (It can work for any poll config, but conveniences have not been supplied for others.) */
public static final AttributeSensor<Void> NO_SENSOR = Sensors.newSensor(Void.class, "brooklyn.no.sensor");
private final AttributeSensor<T> sensor;
private Function<? super V, T> onsuccess;
private Function<? super V, T> onfailure;
private Function<? super Exception, T> onexception;
private Predicate<? super V> checkSuccess;
private boolean suppressDuplicates;
private boolean enabled = true;
// allow 30 seconds before logging at WARN, if there has been no success yet;
// after success WARN immediately
// TODO these should both be configurable
/**
* On startup, the length of time before which a failure can be logged at WARN.
* This grace period is useful to avoid flooding the logs if the feed is expected
* to sometimes be unavailable for a few seconds while the process-under-management
* initialises.
*
* Defaults to 30 seconds.
*/
private Duration logWarningGraceTimeOnStartup = Duration.THIRTY_SECONDS;
/**
* Length of time, after a successful poll, before a subsequent failure can be logged at WARN.
*
* Defaults to 0.
*/
private Duration logWarningGraceTime = Duration.millis(0);
public FeedConfig(AttributeSensor<T> sensor) {
this.sensor = sensor;
}
public FeedConfig(FeedConfig<V, T, F> other) {
this.sensor = other.sensor;
this.onsuccess = other.onsuccess;
this.onfailure = other.onfailure;
this.onexception = other.onexception;
this.checkSuccess = other.checkSuccess;
this.suppressDuplicates = other.suppressDuplicates;
this.logWarningGraceTimeOnStartup = other.logWarningGraceTimeOnStartup;
this.logWarningGraceTime = other.logWarningGraceTime;
this.enabled = other.enabled;
}
@SuppressWarnings("unchecked")
protected F self() {
return (F) this;
}
public AttributeSensor<T> getSensor() {
return sensor;
}
public Predicate<? super V> getCheckSuccess() {
return checkSuccess;
}
public Function<? super V, T> getOnSuccess() {
return onsuccess;
}
public Function<? super V, T> getOnFailure() {
return onfailure;
}
public Function<? super Exception, T> getOnException() {
return onexception;
}
public boolean getSupressDuplicates() {
return suppressDuplicates;
}
public boolean isEnabled() {
return enabled;
}
/** sets the predicate used to check whether a feed run is successful */
public F checkSuccess(Predicate<? super V> val) {
this.checkSuccess = checkNotNull(val, "checkSuccess");
return self();
}
/** as {@link #checkSuccess(Predicate)} */
public F checkSuccess(final Function<? super V,Boolean> val) {
return checkSuccess(Functionals.predicate(val));
}
@SuppressWarnings("unused")
/** @deprecated since 0.7.0, kept for rebind */ @Deprecated
private F checkSuccessLegacy(final Function<? super V,Boolean> val) {
return checkSuccess(new Predicate<V>() {
@Override
public boolean apply(V input) {
return val.apply(input);
}
});
}
public F onSuccess(Function<? super V,T> val) {
this.onsuccess = checkNotNull(val, "onSuccess");
return self();
}
public F setOnSuccess(T val) {
return onSuccess(Functions.constant(val));
}
/**
* A failure is when the connection is fine (no exception) but the other end returns a result object V
* which the feed can tell indicates a failure (e.g. HTTP code 404)
*/
public F onFailure(Function<? super V,T> val) {
this.onfailure = checkNotNull(val, "onFailure");
return self();
}
/** @see #onFailure(Function) */
public F setOnFailure(T val) {
return onFailure(Functions.constant(val));
}
/**
* Registers a callback to be used by {@link #onSuccess(Function)} and {@link #onFailure(Function)},
* i.e. whenever a result comes back, but not in case of exceptions being thrown (ie problems communicating)
*/
public F onResult(Function<? super V, T> val) {
onSuccess(val);
return onFailure(val);
}
/** @see #onResult(Function) */
public F setOnResult(T val) {
return onResult(Functions.constant(val));
}
/**
* An exception is when there is an error in the communication
*/
public F onException(Function<? super Exception,T> val) {
this.onexception = checkNotNull(val, "onException");
return self();
}
/** @see #onException(Function) */
public F setOnException(T val) {
return onException(Functions.constant(val));
}
/**
* A convenience for indicating a behaviour to occur for both {@link #onException(Function)}
* (error connecting) and {@link #onFailure(Function)} (successful communication but failure
* report from remote end)
*/
public F onFailureOrException(Function<Object,T> val) {
onFailure(val);
return onException(val);
}
/** @see #onFailureOrException(Function) */
public F setOnFailureOrException(T val) {
return onFailureOrException(Functions.constant(val));
}
public F suppressDuplicates(boolean val) {
suppressDuplicates = val;
return self();
}
public F logWarningGraceTimeOnStartup(Duration val) {
this.logWarningGraceTimeOnStartup = checkNotNull(val);
return self();
}
public F logWarningGraceTime(Duration val) {
this.logWarningGraceTime = checkNotNull(val);
return self();
}
public Duration getLogWarningGraceTimeOnStartup() {
return logWarningGraceTimeOnStartup;
}
public Duration getLogWarningGraceTime() {
return logWarningGraceTime;
}
/**
* Whether this feed is enabled (defaulting to true).
*/
public F enabled(boolean val) {
enabled = val;
return self();
}
public boolean hasSuccessHandler() {
return this.onsuccess != null;
}
public boolean hasFailureHandler() {
return this.onfailure != null;
}
public boolean hasExceptionHandler() {
return this.onexception != null;
}
public boolean hasCheckSuccessHandler() {
return this.checkSuccess != null;
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append(toStringBaseName());
result.append("[");
boolean contents = false;
Object source = toStringPollSource();
AttributeSensor<T> s = getSensor();
if (Strings.isNonBlank(Strings.toString(source))) {
if (s!=null) {
result.append(s.getName());
result.append(" <- ");
}
result.append(Strings.toUniqueString(source, 40));
contents = true;
} else if (s!=null) {
result.append(s.getName());
contents = true;
}
MutableList<Object> fields = toStringOtherFields();
if (fields!=null) {
for (Object field: fields) {
if (Strings.isNonBlank(Strings.toString(field))) {
if (contents) result.append("; ");
contents = true;
result.append(field);
}
}
}
result.append("]");
return result.toString();
}
/** can be overridden to supply a simpler base name than the class name */
protected String toStringBaseName() {
return JavaClassNames.simpleClassName(this);
}
/** can be overridden to supply add'l info for the {@link #toString()}; subclasses can add to the returned value */
protected MutableList<Object> toStringOtherFields() {
return MutableList.<Object>of();
}
/** can be overridden to supply add'l info for the {@link #toString()}, placed before the sensor with -> */
protected Object toStringPollSource() {
return null;
}
/** all configs should supply a unique tag element, inserted into the feed */
protected String getUniqueTag() {
return toString();
}
/** returns fields which should be used for equality, including by default {@link #toStringOtherFields()} and {@link #toStringPollSource()};
* subclasses can add to the returned value */
protected MutableList<Object> equalsFields() {
MutableList<Object> result = MutableList.of().appendIfNotNull(getSensor()).appendIfNotNull(toStringPollSource());
for (Object field: toStringOtherFields()) result.appendIfNotNull(field);
return result;
}
@Override
public int hashCode() {
int hc = super.hashCode();
for (Object f: equalsFields())
hc = Objects.hashCode(hc, f);
return hc;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!super.equals(obj)) return false;
PollConfig<?,?,?> other = (PollConfig<?,?,?>) obj;
if (!Objects.equal(getUniqueTag(), other.getUniqueTag())) return false;
if (!Objects.equal(equalsFields(), other.equalsFields())) return false;
return true;
}
}