blob: 42295c99d75355ddddb0d331d33ca090273e6924 [file] [log] [blame]
package brooklyn.test;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import org.slf4j.Logger;
import org.testng.Assert;
import brooklyn.util.collections.MutableMap;
import brooklyn.util.time.Duration;
/**
* Repeatedly polls a given URL, to check if it is always available.
*
* @author Alex, Aled
*/
public class WebAppMonitor implements Runnable {
final AtomicBoolean shouldBeActive = new AtomicBoolean(true);
final AtomicBoolean isActive = new AtomicBoolean(false);
final AtomicInteger successes = new AtomicInteger(0);
final AtomicInteger failures = new AtomicInteger(0);
final AtomicLong lastTime = new AtomicLong(-1);
final AtomicReference<Object> lastStatus = new AtomicReference<Object>(null);
final AtomicReference<Object> lastFailure = new AtomicReference<Object>(null);
Logger log;
Object problem = null;
String url;
long delayMillis = 500;
int expectedResponseCode = 200;
public WebAppMonitor(String url) {
this.url = url;
}
public WebAppMonitor() {
}
public WebAppMonitor logFailures(Logger log) {
this.log = log;
return this;
}
public WebAppMonitor delayMillis(long val) {
this.delayMillis = val;
return this;
}
public WebAppMonitor expectedResponseCode(int val) {
this.expectedResponseCode = val;
return this;
}
public WebAppMonitor url(String val) {
this.url = val;
return this;
}
public void run() {
synchronized (isActive) {
if (isActive.getAndSet(true))
throw new IllegalStateException("already running");
}
while (shouldBeActive.get()) {
long startTime = System.currentTimeMillis();
try {
if (preAttempt()) {
int code = HttpTestUtils.getHttpStatusCode(url);
lastTime.set(System.currentTimeMillis() - startTime);
lastStatus.set(code);
if (isResponseOkay(code)) {
successes.incrementAndGet();
} else {
lastFailure.set(code);
failures.incrementAndGet();
onFailure("return code "+code);
}
}
} catch (Exception e) {
lastTime.set(System.currentTimeMillis()-startTime);
lastStatus.set(e);
lastFailure.set(e);
failures.incrementAndGet();
onFailure(e);
}
try {
if (delayMillis > 0) {
Thread.sleep(delayMillis);
}
} catch (InterruptedException e) {
onFailure(e);
shouldBeActive.set(false);
}
}
synchronized (isActive) {
if (!isActive.getAndSet(false))
throw new IllegalStateException("shouldn't be possible!");
isActive.notifyAll();
}
}
public boolean isResponseOkay(Object code) {
return code!=null && new Integer(expectedResponseCode).equals(code);
}
public void setDelayMillis(long delayMillis) {
this.delayMillis = delayMillis;
}
public long getDelayMillis() {
return delayMillis;
}
public void terminate() throws InterruptedException {
shouldBeActive.set(false);
synchronized (isActive) {
while (isActive.get()) isActive.wait();
}
}
public int getFailures() {
return failures.get();
}
public int getSuccesses() {
return successes.get();
}
public void setUrl(String url) {
this.url = url;
}
public String getUrl() {
return url;
}
public Object getProblem() {
return problem;
}
public int getAttempts() {
return getFailures()+getSuccesses();
}
public boolean getLastWasFailed() {
return isResponseOkay(getLastStatus());
}
public Object getLastStatus() {
return lastStatus.get();
}
public long getLastTime() {
return lastTime.get();
}
/** result code (int) or exception */
public Object getLastFailure() {
return lastFailure.get();
}
public void onFailure(Object problem) {
if (log != null) {
log.warn("Detected failure in monitor accessing "+getUrl()+": "+problem);
}
this.problem = problem;
}
/** return false to skip a run */
public boolean preAttempt() {
return true;
}
public WebAppMonitor assertNoFailures(String message) {
return assertSuccessFraction(message, 1.0);
}
public WebAppMonitor assertAttemptsMade(int minAttempts, String message) {
if (getAttempts()<minAttempts) {
Assert.fail(message+" -- webapp access failures! " +
"(0 attempts made; probably blocked on server)");
}
return this;
}
public WebAppMonitor waitForAtLeastOneAttempt() {
return waitForAtLeastOneAttempt(Asserts.DEFAULT_TIMEOUT);
}
public WebAppMonitor waitForAtLeastOneAttempt(Duration timeout) {
Asserts.succeedsEventually(MutableMap.of("timeout", timeout), new Runnable() {
@Override public void run() {
Assert.assertTrue(getAttempts() >= 1);
}});
return this;
}
public WebAppMonitor assertSuccessFraction(String message, double fraction) {
int failures = getFailures();
int attempts = getAttempts();
if ((failures > (1-fraction) * attempts + 0.0001) || attempts <= 0) {
Assert.fail(message+" -- webapp access failures! " +
"("+failures+" failed of "+attempts+" monitoring attempts) against "+getUrl()+"; " +
"last was "+getLastStatus()+" taking "+getLastTime()+"ms" +
(getLastFailure() != null ? "; last failure was "+getLastFailure() : ""));
}
return this;
}
public WebAppMonitor resetCounts() {
failures.set(0);
successes.set(0);
return this;
}
}