Skip to content

Instantly share code, notes, and snippets.

@nipafx
Created April 28, 2021 08:31
Show Gist options
  • Save nipafx/a21f1d1c5211617453e43a88c9df439c to your computer and use it in GitHub Desktop.
Save nipafx/a21f1d1c5211617453e43a88c9df439c to your computer and use it in GitHub Desktop.
Partial implementation of `DisabledIfUnreachable`
import org.junit.jupiter.api.extension.ExtendWith;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(EnabledIfReachableCondition.class)
public @interface EnabledIfReachable {
String url();
int timeoutMillis();
}
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.jupiter.api.extension.ExtensionContext;
import java.io.IOException;
import java.lang.reflect.AnnotatedElement;
import java.net.HttpURLConnection;
import java.net.URL;
import static java.lang.String.format;
import static org.junit.jupiter.api.extension.ConditionEvaluationResult.disabled;
import static org.junit.jupiter.api.extension.ConditionEvaluationResult.enabled;
import static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;
class EnabledIfReachableCondition implements ExecutionCondition {
private static final ConditionEvaluationResult ENABLED_BY_DEFAULT =
enabled("@EnabledIfReachable is not present");
@Override
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
AnnotatedElement element = context
.getElement()
.orElseThrow(IllegalStateException::new);
return findAnnotation(element, EnabledIfReachable.class)
.map(annotation -> disableIfUnreachable(annotation, element))
.orElse(ENABLED_BY_DEFAULT);
}
private ConditionEvaluationResult disableIfUnreachable(EnabledIfReachable annotation, AnnotatedElement element) {
String url = annotation.url();
int timeoutMillis = annotation.timeoutMillis();
boolean reachable = pingUrl(url, timeoutMillis);
if (reachable)
return enabled(format("%s is enabled because %s is reachable", element, url));
else
return disabled(format(
"%s is disabled because %s could not be reached in %dms",
element, url, timeoutMillis));
}
/**
* Pings a HTTP URL. This effectively sends a HEAD request and returns
* <code>true</code> if the response code is in the 200-399 range.
*
* (From BalusC on StackOverflow:
* - https://stackoverflow.com/a/3584332/2525313
* - https://stackoverflow.com/users/157882/balusc)
*
* @param url
* The HTTP URL to be pinged.
* @param timeoutMillis
* The timeout in millis for both the connection timeout and the
* response read timeout. Note that the total timeout is effectively
* two times the given timeout.
*
* @return <code>true</code> if the given HTTP URL has returned response
* code 200-399 on a HEAD request within the given timeout, otherwise
* <code>false</code>.
*/
private static boolean pingUrl(String url, int timeoutMillis) {
// Otherwise an exception may be thrown on invalid SSL certificates
String httpUrl = url.replaceFirst("^https", "http");
try {
HttpURLConnection connection = (HttpURLConnection) new URL(httpUrl).openConnection();
connection.setConnectTimeout(timeoutMillis);
connection.setReadTimeout(timeoutMillis);
connection.setRequestMethod("HEAD");
int responseCode = connection.getResponseCode();
return (200 <= responseCode && responseCode <= 399);
} catch (IOException exception) {
return false;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment