blob: d0c6487325b7b7e03c14c41bc9c07ab7b3d980b9 [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.jclouds.util;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.jclouds.util.Throwables2.getFirstThrowableOfType;
import java.util.Date;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.annotation.Resource;
import org.jclouds.logging.Logger;
import com.google.common.base.Predicate;
public class Predicates2 {
/** Returns a predicate that evaluates to true if the String being tested starts with a prefix. */
public static Predicate<String> startsWith(final String prefix) {
return new Predicate<String>() {
@Override
public boolean apply(final String input) {
return input.startsWith(prefix);
}
@Override
public String toString() {
return "startsWith(" + prefix + ")";
}
};
}
/**
* Retries a predicate until it is met, a timeout occurs, or an exception occurs.
*/
public static <T> Predicate<T> retry(Predicate<T> findOrBreak, long timeout, long period, long maxPeriod,
TimeUnit unit) {
return new RetryablePredicate<T>(findOrBreak, timeout, period, maxPeriod, unit);
}
/**
* like {@link #retry(Predicate, long, long, long, TimeUnit)} where {@code maxPeriod} is 10x {@code period}
*/
public static <T> Predicate<T> retry(Predicate<T> findOrBreak, long timeout, long period, TimeUnit unit) {
return retry(findOrBreak, timeout, period, period * 10L, unit);
}
/**
* like {@link #retry(Predicate, long, long, long, TimeUnit)} where {@code unit} is in milliseconds
*/
public static <T> Predicate<T> retry(Predicate<T> findOrBreak, long timeout, long period, long maxPeriod) {
return retry(findOrBreak, timeout, period, maxPeriod, MILLISECONDS);
}
/**
* @see org.jclouds.compute.config.ComputeServiceProperties#POLL_INITIAL_PERIOD
*/
public static final long DEFAULT_PERIOD = 50L;
/**
* @see org.jclouds.compute.config.ComputeServiceProperties#POLL_MAX_PERIOD
*/
public static final long DEFAULT_MAX_PERIOD = 1000L;
/**
* like {@link #retry(Predicate, long, long, long, TimeUnit)} where {@code unit} is in milliseconds, {@code period}
* is 50ms, and {@code maxPeriod} 1s.
*/
public static <T> Predicate<T> retry(Predicate<T> findOrBreak, long timeout) {
return retry(findOrBreak, timeout, DEFAULT_PERIOD, DEFAULT_MAX_PERIOD, MILLISECONDS);
}
private static class RetryablePredicate<T> implements Predicate<T> {
private final long timeout;
private final long period;
private final long maxPeriod;
private final Predicate<T> findOrBreak;
@Resource
protected Logger logger = Logger.NULL;
protected RetryablePredicate(Predicate<T> findOrBreak, long timeout, long period, long maxPeriod, TimeUnit unit) {
this.findOrBreak = findOrBreak;
this.timeout = unit.toMillis(timeout);
this.period = unit.toMillis(period);
this.maxPeriod = unit.toMillis(maxPeriod);
}
@Override
public boolean apply(T input) {
// Ensure that try one last time at the end (e.g. if timeout is 30 secs and last sleep
// takes us up to the 30 secs mark, then test again rather than returning immediately
// after the sleep!).
// Always try at least once, even if timeout is 0 or negative.
// A timeout of 0 means 0 millis.
try {
long i = 1L;
long now = System.currentTimeMillis();
long end = now + timeout;
while (now < end) {
if (findOrBreak.apply(input)) {
return true;
}
long sleepTime = nextMaxInterval(i++, end);
if (sleepTime > 0) Thread.sleep(sleepTime);
now = System.currentTimeMillis();
}
return findOrBreak.apply(input);
} catch (InterruptedException e) {
logger.warn(e, "predicate %s on %s interrupted, returning false", input, findOrBreak);
Thread.currentThread().interrupt();
return false;
} catch (RuntimeException e) {
if (getFirstThrowableOfType(e, ExecutionException.class) != null) {
logger.warn(e, "predicate %s on %s errored [%s], returning false", input, findOrBreak, e.getMessage());
return false;
} else if (getFirstThrowableOfType(e, IllegalStateException.class) != null) {
logger.warn(e, "predicate %s on %s illegal state [%s], returning false", input, findOrBreak, e.getMessage());
return false;
} else if (getFirstThrowableOfType(e, CancellationException.class) != null) {
logger.warn(e, "predicate %s on %s cancelled [%s], returning false", input, findOrBreak, e.getMessage());
return false;
} else if (getFirstThrowableOfType(e, TimeoutException.class) != null) {
logger.warn(e, "predicate %s on %s timed out [%s], returning false", input, findOrBreak, e.getMessage());
return false;
} else
throw e;
}
}
/**
* @deprecated since 1.9.0; use {@link #nextMaxInterval(long, long)}
*/
@Deprecated
protected long nextMaxInterval(long attempt, Date end) {
return nextMaxInterval(attempt, end.getTime());
}
/**
* Calculates the time interval to a retry attempt.<p>
* The interval increases exponentially with each attempt, at a rate of nextInterval *= 1.5
* (where 1.5 is the backoff factor), to the maximum interval or specified timeout.
*
* @param attempt number of this attempt (starting at 1 for the first retry)
* @param endTime timeout
* @return time in milliseconds from now until the next attempt, or if negative, time lapsed
* since the specified timeout
*/
protected long nextMaxInterval(long attempt, long endTime) {
long interval = (long) (period * Math.pow(1.5, attempt - 1));
interval = interval > maxPeriod ? maxPeriod : interval;
long max = endTime - System.currentTimeMillis();
return (interval > max) ? max : interval;
}
/**
* @deprecated since 1.9.0; use {@link #nextMaxInterval(long, long)}
*/
@Deprecated
protected boolean before(Date end) {
return new Date().compareTo(end) < 0;
}
/**
* @deprecated since 1.9.0; use {@link #nextMaxInterval(long, long)}
*/
@Deprecated
protected boolean atOrAfter(Date end) {
return new Date().compareTo(end) >= 0;
}
}
}