pulled in stabilization changes from master

This commit is contained in:
Adrian Cole 2011-05-14 19:42:27 -07:00
parent 5afb5fecfa
commit 838c346190
6 changed files with 297 additions and 160 deletions

View File

@ -106,7 +106,7 @@ public class BackoffLimitedRetryHandler implements HttpRetryHandler, IOException
return false;
} else if (command.getFailureCount() > retryCountLimit) {
logger.warn("Cannot retry after server error, command has exceeded retry limit %1$d: %2$s", retryCountLimit,
command);
command);
return false;
} else {
imposeBackoffExponentialDelay(command.getFailureCount(), "server error: " + command.toString());
@ -119,8 +119,14 @@ public class BackoffLimitedRetryHandler implements HttpRetryHandler, IOException
}
public void imposeBackoffExponentialDelay(long period, int pow, int failureCount, int max, String commandDescription) {
imposeBackoffExponentialDelay(period, period * 10l, pow, failureCount, max, commandDescription);
}
public void imposeBackoffExponentialDelay(long period, long maxPeriod, int pow, int failureCount, int max,
String commandDescription) {
long delayMs = (long) (period * Math.pow(failureCount, pow));
logger.debug("Retry %d/%d: delaying for %d ms: %s", failureCount, retryCountLimit, delayMs, commandDescription);
delayMs = delayMs > maxPeriod ? maxPeriod : delayMs;
logger.debug("Retry %d/%d: delaying for %d ms: %s", failureCount, max, delayMs, commandDescription);
try {
Thread.sleep(delayMs);
} catch (InterruptedException e) {
@ -129,3 +135,4 @@ public class BackoffLimitedRetryHandler implements HttpRetryHandler, IOException
}
}

View File

@ -18,11 +18,13 @@
*/
package org.jclouds.http.internal;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Throwables.propagate;
import static com.google.common.collect.Iterables.getLast;
import static com.google.common.io.ByteStreams.toByteArray;
import static com.google.common.io.Closeables.closeQuietly;
import static org.jclouds.io.Payloads.newInputStreamPayload;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@ -59,14 +61,13 @@ import org.jclouds.http.handlers.DelegatingErrorHandler;
import org.jclouds.http.handlers.DelegatingRetryHandler;
import org.jclouds.io.MutableContentMetadata;
import org.jclouds.io.Payload;
import org.jclouds.io.Payloads;
import org.jclouds.logging.Logger;
import org.jclouds.rest.internal.RestAnnotationProcessor;
import com.google.common.base.Supplier;
import com.google.common.base.Throwables;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableMultimap.Builder;
import com.google.common.io.CountingOutputStream;
/**
* Basic implementation of a {@link HttpCommandExecutorService}.
@ -85,11 +86,11 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe
@Inject
public JavaUrlHttpCommandExecutorService(HttpUtils utils,
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioWorkerExecutor,
DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler,
DelegatingErrorHandler errorHandler, HttpWire wire, @Named("untrusted") HostnameVerifier verifier,
@Named("untrusted") Supplier<SSLContext> untrustedSSLContextProvider) throws SecurityException,
NoSuchFieldException {
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioWorkerExecutor,
DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler,
DelegatingErrorHandler errorHandler, HttpWire wire, @Named("untrusted") HostnameVerifier verifier,
@Named("untrusted") Supplier<SSLContext> untrustedSSLContextProvider) throws SecurityException,
NoSuchFieldException {
super(utils, ioWorkerExecutor, retryHandler, ioRetryHandler, errorHandler, wire);
if (utils.getMaxConnections() > 0)
System.setProperty("http.maxConnections", String.valueOf(checkNotNull(utils, "utils").getMaxConnections()));
@ -101,6 +102,7 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe
@Override
protected HttpResponse invoke(HttpURLConnection connection) throws IOException, InterruptedException {
HttpResponse.Builder builder = HttpResponse.builder();
InputStream in = null;
try {
in = consumeOnClose(connection.getInputStream());
@ -112,19 +114,28 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe
assert false : "should have propagated exception";
}
if (connection.getResponseCode() == 204) {
int responseCode = connection.getResponseCode();
if (responseCode == 204) {
closeQuietly(in);
in = null;
}
Multimap<String, String> headers = LinkedHashMultimap.create();
builder.statusCode(responseCode);
builder.message(connection.getResponseMessage());
Builder<String, String> headerBuilder = ImmutableMultimap.<String, String> builder();
for (String header : connection.getHeaderFields().keySet()) {
headers.putAll(header, connection.getHeaderFields().get(header));
// HTTP message comes back as a header without a key
if (header != null)
headerBuilder.putAll(header, connection.getHeaderFields().get(header));
}
Payload payload = in != null ? Payloads.newInputStreamPayload(in) : null;
if (payload != null)
ImmutableMultimap<String, String> headers = headerBuilder.build();
Payload payload = in != null ? newInputStreamPayload(in) : null;
if (payload != null) {
payload.getContentMetadata().setPropertiesFromHttpHeaders(headers);
return new HttpResponse(connection.getResponseCode(), connection.getResponseMessage(), payload,
RestAnnotationProcessor.filterOutContentHeaders(headers));
builder.payload(payload);
}
builder.headers(RestAnnotationProcessor.filterOutContentHeaders(headers));
return builder.build();
}
private InputStream bufferAndCloseStream(InputStream inputStream) throws IOException {
@ -171,6 +182,12 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe
if (utils.trustAllCerts())
sslCon.setSSLSocketFactory(untrustedSSLContextProvider.get().getSocketFactory());
}
if (utils.getConnectionTimeout() > 0) {
connection.setConnectTimeout(utils.getConnectionTimeout());
}
if (utils.getSocketOpenTimeout() > 0) {
connection.setReadTimeout(utils.getSocketOpenTimeout());
}
connection.setDoOutput(true);
connection.setAllowUserInteraction(false);
// do not follow redirects since https redirects don't work properly
@ -184,7 +201,7 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe
methodField.set(connection, request.getMethod());
} catch (Exception e1) {
logger.error(e, "could not set request method: ", request.getMethod());
Throwables.propagate(e1);
propagate(e1);
}
}
@ -213,14 +230,20 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe
} else {
Long length = checkNotNull(md.getContentLength(), "payload.getContentLength");
connection.setRequestProperty(HttpHeaders.CONTENT_LENGTH, length.toString());
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6755625
checkArgument(length < Integer.MAX_VALUE,
"JDK 1.6 does not support >2GB chunks. Use chunked encoding, if possible.");
connection.setFixedLengthStreamingMode(length.intValue());
if (length.intValue() > 0) {
connection.setRequestProperty("Expect", "100-continue");
}
}
// writeTo will close the output stream
CountingOutputStream out = new CountingOutputStream(connection.getOutputStream());
try {
request.getPayload().writeTo(connection.getOutputStream());
request.getPayload().writeTo(out);
} catch (IOException e) {
e.printStackTrace();
throw e;
throw new RuntimeException(String.format("error after writing %d/%s bytes to %s", out.getCount(), md
.getContentLength(), request.getRequestLine()), e);
}
} else {
connection.setRequestProperty(HttpHeaders.CONTENT_LENGTH, "0");

View File

@ -18,8 +18,12 @@
*/
package org.jclouds.predicates;
import static org.jclouds.util.Throwables2.getFirstThrowableOfType;
import java.util.Date;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.annotation.Resource;
@ -36,30 +40,33 @@ import com.google.common.base.Predicate;
public class RetryablePredicate<T> implements Predicate<T> {
private final long maxWait;
private final long period;
private final long maxPeriod;
private final Predicate<T> predicate;
@Resource
protected Logger logger = Logger.NULL;
public RetryablePredicate(Predicate<T> predicate, long maxWait, long period,
TimeUnit unit) {
public RetryablePredicate(Predicate<T> predicate, long maxWait, long period, long maxPeriod, TimeUnit unit) {
this.predicate = predicate;
this.maxWait = unit.toMillis(maxWait);
this.period = unit.toMillis(period);
this.maxPeriod = unit.toMillis(maxPeriod);
}
public RetryablePredicate(Predicate<T> predicate, long maxWait, long period, TimeUnit unit) {
this(predicate, maxWait, period, period * 10l, unit);
}
public RetryablePredicate(Predicate<T> predicate, long maxWait) {
this.predicate = predicate;
this.maxWait = maxWait;
this.period = 50l;
this(predicate, maxWait, 50l, 1000l, TimeUnit.MILLISECONDS);
}
@Override
public boolean apply(T input) {
try {
long i = 1l;
for (Date end = new Date(System.currentTimeMillis() + maxWait); before(end); Thread
.sleep(nextMaxInterval(i++, end))) {
for (Date end = new Date(System.currentTimeMillis() + maxWait); before(end); Thread.sleep(nextMaxInterval(i++,
end))) {
if (predicate.apply(input)) {
return true;
} else if (atOrAfter(end)) {
@ -67,14 +74,26 @@ public class RetryablePredicate<T> implements Predicate<T> {
}
}
} catch (InterruptedException e) {
logger.warn(e, "predicate %s on %s interrupted, returning false",
input, predicate);
logger.warn(e, "predicate %s on %s interrupted, returning false", input, predicate);
} catch (RuntimeException e) {
if (getFirstThrowableOfType(e, ExecutionException.class) != null) {
logger.warn(e, "predicate %s on %s errored [%s], returning false", input, predicate, e.getMessage());
return false;
} else if (getFirstThrowableOfType(e, IllegalStateException.class) != null) {
logger.warn(e, "predicate %s on %s illegal state [%s], returning false", input, predicate, e.getMessage());
return false;
} else if (getFirstThrowableOfType(e, TimeoutException.class) != null) {
logger.warn(e, "predicate %s on %s timed out [%s], returning false", input, predicate, e.getMessage());
return false;
} else
throw e;
}
return false;
}
long nextMaxInterval(long attempt, Date end) {
long interval = (period * (long) Math.pow(attempt, 1.5));
interval = interval > maxPeriod ? maxPeriod : interval;
long max = end.getTime() - System.currentTimeMillis();
return (interval > max) ? max : interval;
}
@ -87,3 +106,4 @@ public class RetryablePredicate<T> implements Predicate<T> {
return new Date().compareTo(end) >= 0;
}
}

View File

@ -63,6 +63,7 @@ import org.testng.annotations.BeforeTest;
import org.testng.annotations.Optional;
import org.testng.annotations.Parameters;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
@ -86,7 +87,7 @@ public abstract class BaseJettyTest {
static final Pattern actionPattern = Pattern.compile("/objects/(.*)/action/([a-z]*);?(.*)");
@BeforeTest
@Parameters({ "test-jetty-port" })
@Parameters( { "test-jetty-port" })
public void setUpJetty(@Optional("8123") final int testPort) throws Exception {
this.testPort = testPort;
@ -96,62 +97,69 @@ public abstract class BaseJettyTest {
Handler server1Handler = new AbstractHandler() {
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
if (failIfNoContentLength(request, response)) {
return;
} else if (target.indexOf("sleep") > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
propagate(e);
}
response.setContentType("text/xml");
response.setStatus(HttpServletResponse.SC_OK);
} else if (target.indexOf("redirect") > 0) {
response.sendRedirect("https://localhost:" + (testPort + 1) + "/");
} else if (target.indexOf("101constitutions") > 0) {
response.setContentType("text/plain");
response.setHeader("Content-MD5", md5);
response.setStatus(HttpServletResponse.SC_OK);
copy(oneHundredOneConstitutions.getInput(), response.getOutputStream());
} else if (request.getMethod().equals("PUT")) {
if (request.getContentLength() > 0) {
throws IOException, ServletException {
InputStream body = request.getInputStream();
try {
if (failIfNoContentLength(request, response)) {
return;
} else if (target.indexOf("sleep") > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
propagate(e);
}
response.setContentType("text/xml");
response.setStatus(HttpServletResponse.SC_OK);
} else if (target.indexOf("redirect") > 0) {
response.sendRedirect("https://localhost:" + (testPort + 1) + "/");
} else if (target.indexOf("101constitutions") > 0) {
response.setContentType("text/plain");
response.setHeader("Content-MD5", md5);
response.setStatus(HttpServletResponse.SC_OK);
copy(oneHundredOneConstitutions.getInput(), response.getOutputStream());
} else if (request.getMethod().equals("PUT")) {
if (request.getContentLength() > 0) {
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println(Strings2.toStringAndClose(body) + "PUT");
} else {
response.sendError(500, "no content");
}
} else if (request.getMethod().equals("POST")) {
// don't redirect large objects
if (request.getContentLength() < 10240 && redirectEveryTwentyRequests(request, response))
return;
if (failEveryTenRequests(request, response))
return;
if (request.getContentLength() > 0) {
handlePost(request, response);
} else {
handleAction(request, response);
}
} else if (request.getHeader("range") != null) {
response.sendError(404, "no content");
} else if (request.getHeader("test") != null) {
response.setContentType("text/plain");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println("test");
} else if (request.getMethod().equals("HEAD")) {
/*
* NOTE: by HTML specification, HEAD response MUST NOT include a body
*/
response.setContentType("text/xml");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println(Strings2.toStringAndClose(request.getInputStream()) + "PUT");
} else {
response.sendError(500, "no content");
if (failEveryTenRequests(request, response))
return;
response.setContentType("text/xml");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println(XML);
}
} else if (request.getMethod().equals("POST")) {
// don't redirect large objects
if (request.getContentLength() < 10240 && redirectEveryTwentyRequests(request, response))
return;
if (failEveryTenRequests(request, response))
return;
if (request.getContentLength() > 0) {
handlePost(request, response);
} else {
handleAction(request, response);
}
} else if (request.getHeader("range") != null) {
response.sendError(404, "no content");
} else if (request.getHeader("test") != null) {
response.setContentType("text/plain");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println("test");
} else if (request.getMethod().equals("HEAD")) {
/*
* NOTE: by HTML specification, HEAD response MUST NOT include a body
*/
response.setContentType("text/xml");
response.setStatus(HttpServletResponse.SC_OK);
} else {
if (failEveryTenRequests(request, response))
return;
response.setContentType("text/xml");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println(XML);
((Request) request).setHandled(true);
} catch (IOException e) {
if (body != null)
closeQuietly(body);
response.sendError(500, Throwables.getStackTraceAsString(e));
}
((Request) request).setHandled(true);
}
};
@ -172,11 +180,12 @@ public abstract class BaseJettyTest {
}
private static void handlePost(HttpServletRequest request, HttpServletResponse response) throws IOException {
InputStream body = request.getInputStream();
try {
if (request.getHeader("Content-MD5") != null) {
String expectedMd5 = request.getHeader("Content-MD5");
String realMd5FromRequest;
realMd5FromRequest = CryptoStreams.md5Base64(InputSuppliers.of(request.getInputStream()));
realMd5FromRequest = CryptoStreams.md5Base64(InputSuppliers.of(body));
boolean matched = expectedMd5.equals(realMd5FromRequest);
if (matched) {
response.setStatus(HttpServletResponse.SC_OK);
@ -185,52 +194,60 @@ public abstract class BaseJettyTest {
response.sendError(500, "didn't match");
}
} else {
String responseString = (request.getContentLength() < 10240) ? Strings2.toStringAndClose(body) + "POST"
: "POST";
body = null;
for (String header : new String[] { "Content-Disposition", HttpHeaders.CONTENT_LANGUAGE,
HttpHeaders.CONTENT_ENCODING })
HttpHeaders.CONTENT_ENCODING })
if (request.getHeader(header) != null) {
response.addHeader("x-" + header, request.getHeader(header));
}
response.setStatus(HttpServletResponse.SC_OK);
String responseString = "POST";
if (request.getContentLength() < 10240) {
responseString = Strings2.toStringAndClose(request.getInputStream()) + "POST";
} else {
closeQuietly(request.getInputStream());
}
response.getWriter().println(responseString);
}
} catch (IOException e) {
response.sendError(500, e.toString());
if (body != null)
closeQuietly(body);
response.sendError(500, Throwables.getStackTraceAsString(e));
}
}
protected void setupAndStartSSLServer(final int testPort) throws Exception {
Handler server2Handler = new AbstractHandler() {
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
if (request.getMethod().equals("PUT")) {
if (request.getContentLength() > 0) {
throws IOException, ServletException {
InputStream body = request.getInputStream();
try {
if (request.getMethod().equals("PUT")) {
String text = Strings2.toStringAndClose(body);
body = null;
if (request.getContentLength() > 0) {
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println(text + "PUTREDIRECT");
}
} else if (request.getMethod().equals("POST")) {
if (request.getContentLength() > 0) {
handlePost(request, response);
} else {
handleAction(request, response);
}
} else if (request.getMethod().equals("HEAD")) {
/*
* NOTE: by HTML specification, HEAD response MUST NOT include a body
*/
response.setContentType("text/xml");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println(Strings2.toStringAndClose(request.getInputStream()) + "PUTREDIRECT");
}
} else if (request.getMethod().equals("POST")) {
if (request.getContentLength() > 0) {
handlePost(request, response);
} else {
handleAction(request, response);
response.setContentType("text/xml");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println(XML2);
}
} else if (request.getMethod().equals("HEAD")) {
/*
* NOTE: by HTML specification, HEAD response MUST NOT include a body
*/
response.setContentType("text/xml");
response.setStatus(HttpServletResponse.SC_OK);
} else {
response.setContentType("text/xml");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println(XML2);
((Request) request).setHandled(true);
} catch (IOException e) {
if (body != null)
closeQuietly(body);
response.sendError(500, Throwables.getStackTraceAsString(e));
}
((Request) request).setHandled(true);
}
};
@ -261,12 +278,12 @@ public abstract class BaseJettyTest {
}
public static RestContextBuilder<IntegrationTestClient, IntegrationTestAsyncClient> newBuilder(int testPort,
Properties properties, Module... connectionModules) {
Properties properties, Module... connectionModules) {
properties.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
properties.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
RestContextSpec<IntegrationTestClient, IntegrationTestAsyncClient> contextSpec = contextSpec("test",
"http://localhost:" + testPort, "1", "", "identity", null, IntegrationTestClient.class,
IntegrationTestAsyncClient.class, ImmutableSet.<Module> copyOf(connectionModules));
"http://localhost:" + testPort, "1", "", "identity", null, IntegrationTestClient.class,
IntegrationTestAsyncClient.class, ImmutableSet.<Module> copyOf(connectionModules));
return createContextBuilder(contextSpec, properties);
}
@ -300,7 +317,7 @@ public abstract class BaseJettyTest {
}
protected boolean redirectEveryTwentyRequests(HttpServletRequest request, HttpServletResponse response)
throws IOException {
throws IOException {
if (cycle.incrementAndGet() % 20 == 0) {
response.sendRedirect("http://localhost:" + (testPort + 1) + "/");
((Request) request).setHandled(true);

View File

@ -60,38 +60,40 @@ public class BackoffLimitedRetryHandlerTest {
BackoffLimitedRetryHandler handler = new BackoffLimitedRetryHandler();
@Test
void testExponentialBackoffDelay() throws InterruptedException {
long acceptableDelay = 25; // Delay to forgive if tests run long.
void testExponentialBackoffDelayDefaultMaxInterval500() throws InterruptedException {
long period = 100;
long acceptableDelay = period - 1;
long startTime = System.nanoTime();
handler.imposeBackoffExponentialDelay(1, "TEST FAILURE: 1");
handler.imposeBackoffExponentialDelay(period, 2, 1, 5, "TEST FAILURE: 1");
long elapsedTime = (System.nanoTime() - startTime) / 1000000;
assert (elapsedTime >= 49) : elapsedTime;
assertTrue(elapsedTime < 50 + acceptableDelay);
assert (elapsedTime >= period - 1) : elapsedTime;
assertTrue(elapsedTime < period + acceptableDelay);
startTime = System.nanoTime();
handler.imposeBackoffExponentialDelay(2, "TEST FAILURE: 2");
handler.imposeBackoffExponentialDelay(period, 2, 2, 5, "TEST FAILURE: 2");
elapsedTime = (System.nanoTime() - startTime) / 1000000;
assert (elapsedTime >= 199) : elapsedTime;
assertTrue(elapsedTime < 200 + acceptableDelay);
assert (elapsedTime >= period * 4 - 1) : elapsedTime;
assertTrue(elapsedTime < period * 9);
startTime = System.nanoTime();
handler.imposeBackoffExponentialDelay(3, "TEST FAILURE: 3");
handler.imposeBackoffExponentialDelay(period, 2, 3, 5, "TEST FAILURE: 3");
elapsedTime = (System.nanoTime() - startTime) / 1000000;
assert (elapsedTime >= 449) : elapsedTime;
assertTrue(elapsedTime < 450 + acceptableDelay);
assert (elapsedTime >= period * 9 - 1) : elapsedTime;
assertTrue(elapsedTime < period * 10);
startTime = System.nanoTime();
handler.imposeBackoffExponentialDelay(4, "TEST FAILURE: 4");
handler.imposeBackoffExponentialDelay(period, 2, 4, 5, "TEST FAILURE: 4");
elapsedTime = (System.nanoTime() - startTime) / 1000000;
assert (elapsedTime >= 799) : elapsedTime;
assertTrue(elapsedTime < 800 + acceptableDelay * 2);
assert (elapsedTime >= period * 10 - 1) : elapsedTime;
assertTrue(elapsedTime < period * 11);
startTime = System.nanoTime();
handler.imposeBackoffExponentialDelay(5, "TEST FAILURE: 5");
handler.imposeBackoffExponentialDelay(period, 2, 5, 5, "TEST FAILURE: 5");
elapsedTime = (System.nanoTime() - startTime) / 1000000;
assert (elapsedTime >= 1249) : elapsedTime;
assertTrue(elapsedTime < 1250 + acceptableDelay * 2);
assert (elapsedTime >= period * 10 - 1) : elapsedTime;
assertTrue(elapsedTime < period * 11);
}
TransformingHttpCommandExecutorServiceImpl executorService;

View File

@ -19,12 +19,15 @@
package org.jclouds.predicates;
import java.util.Date;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.testng.annotations.Test;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Supplier;
/**
*
@ -33,58 +36,123 @@ import com.google.common.base.Predicates;
*/
@Test(groups = "unit", sequential = true)
public class RetryablePredicateTest {
public static int SLOW_BUILD_SERVER_GRACE = 100;
@Test
void testFalseOnIllegalStateExeception() {
ensureImmediateReturnFor(new IllegalStateException());
}
@SuppressWarnings("serial")
@Test
void testFalseOnExecutionException() {
ensureImmediateReturnFor(new ExecutionException() {
});
}
@SuppressWarnings("serial")
@Test
void testFalseOnTimeoutException() {
ensureImmediateReturnFor(new TimeoutException() {
});
}
@SuppressWarnings("serial")
@Test(expectedExceptions = RuntimeException.class)
void testPropagateOnException() {
ensureImmediateReturnFor(new Exception() {
});
}
private void ensureImmediateReturnFor(final Exception ex) {
RetryablePredicate<Supplier<String>> predicate = new RetryablePredicate<Supplier<String>>(
new Predicate<Supplier<String>>() {
@Override
public boolean apply(Supplier<String> input) {
return "goo".equals(input.get());
}
}, 3, 1, TimeUnit.SECONDS);
Date startPlusThird = new Date(System.currentTimeMillis() + 1000);
assert !predicate.apply(new Supplier<String>() {
@Override
public String get() {
throw new RuntimeException(ex);
}
});
Date now = new Date();
assert now.compareTo(startPlusThird) < 0 : String.format("%s should be less than %s", now.getTime(),
startPlusThird.getTime());
}
@Test
void testAlwaysTrue() {
RetryablePredicate<String> predicate = new RetryablePredicate<String>(Predicates
.<String> alwaysTrue(), 3, 1, TimeUnit.SECONDS);
Date startPlusSecond = new Date(System.currentTimeMillis() + 1000);
RetryablePredicate<String> predicate = new RetryablePredicate<String>(Predicates.<String> alwaysTrue(), 3, 1,
TimeUnit.SECONDS);
Date startPlusThird = new Date(System.currentTimeMillis() + 1000);
predicate.apply("");
Date now = new Date();
assert now.compareTo(startPlusSecond) < 0 : String.format("%s should be less than %s", now,
startPlusSecond);
assert now.compareTo(startPlusThird) < 0 : String.format("%s should be less than %s", now.getTime(),
startPlusThird.getTime());
}
@Test
void testAlwaysFalseMillis() {
RetryablePredicate<String> predicate = new RetryablePredicate<String>(Predicates
.<String> alwaysFalse(), 3, 1, TimeUnit.SECONDS);
RetryablePredicate<String> predicate = new RetryablePredicate<String>(Predicates.<String> alwaysFalse(), 3, 1,
TimeUnit.SECONDS);
Date startPlus3Seconds = new Date(System.currentTimeMillis() + 3000);
Date startPlus4Seconds = new Date(System.currentTimeMillis() + 4000);
Date startPlus4Seconds = new Date(System.currentTimeMillis() + 4000 + SLOW_BUILD_SERVER_GRACE);
predicate.apply("");
Date now = new Date();
assert now.compareTo(startPlus3Seconds) >= 0 : String.format("%s should be less than %s",
startPlus3Seconds, now);
assert now.compareTo(startPlus4Seconds) <= 0 : String.format("%s should be greater than %s",
startPlus4Seconds, now);
assert now.compareTo(startPlus3Seconds) >= 0 : String.format("%s should be less than %s", startPlus3Seconds
.getTime(), now.getTime());
assert now.compareTo(startPlus4Seconds) <= 0 : String.format("%s should be greater than %s", startPlus4Seconds
.getTime(), now.getTime());
}
private static class SecondTimeTrue implements Predicate<String> {
private static class ThirdTimeTrue implements Predicate<String> {
private int count = 0;
@Override
public boolean apply(String input) {
return count++ == 1;
return count++ == 2;
}
}
@Test
void testSecondTimeTrue() {
RetryablePredicate<String> predicate = new RetryablePredicate<String>(new SecondTimeTrue(),
3, 1, TimeUnit.SECONDS);
void testThirdTimeTrue() {
RetryablePredicate<String> predicate = new RetryablePredicate<String>(new ThirdTimeTrue(), 3, 1, TimeUnit.SECONDS);
Date startPlusSecond = new Date(System.currentTimeMillis() + 1000);
Date startPlus2Seconds = new Date(System.currentTimeMillis() + 2000);
Date startPlus = new Date(System.currentTimeMillis() + 1000);
Date startPlus3 = new Date(System.currentTimeMillis() + 3000 + SLOW_BUILD_SERVER_GRACE);
predicate.apply("");
Date now = new Date();
assert now.compareTo(startPlusSecond) >= 0 : String.format("%s should be greater than %s",
now, startPlusSecond);
assert now.compareTo(startPlus2Seconds) <= 0 : String.format("%s should be greater than %s",
startPlus2Seconds, now);
assert now.compareTo(startPlus) >= 0 : String.format("%s should be greater than %s", now.getTime(), startPlus
.getTime());
assert now.compareTo(startPlus3) <= 0 : String.format("%s should be greater than %s", startPlus3.getTime(), now
.getTime());
}
@Test
void testThirdTimeTrueLimitedMaxInterval() {
RetryablePredicate<String> predicate = new RetryablePredicate<String>(new ThirdTimeTrue(), 3, 1, 1,
TimeUnit.SECONDS);
Date startPlus = new Date(System.currentTimeMillis() + 1000);
Date startPlus2 = new Date(System.currentTimeMillis() + 2000 + SLOW_BUILD_SERVER_GRACE);
predicate.apply("");
Date now = new Date();
assert now.compareTo(startPlus) >= 0 : String.format("%s should be greater than %s", now.getTime(), startPlus
.getTime());
assert now.compareTo(startPlus2) <= 0 : String.format("%s should be greater than %s", startPlus2.getTime(), now
.getTime());
}
}