| /* |
| * 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; |
| } |
| } |