Skip to content

Commit

Permalink
Features:
Browse files Browse the repository at this point in the history
now RetryerContext is mandatory and should be injected to atlas config
Re-designed Retryer's, now we use global RetryerContext as default
ISExtension (for momentary checking)
now beforeMethod return configuration instance for alteration

fixes:
qameta#81
qameta#79
qameta#74
  • Loading branch information
I301235 committed Jul 4, 2019
1 parent 80f5b6a commit 1340d7e
Show file tree
Hide file tree
Showing 14 changed files with 327 additions and 107 deletions.
12 changes: 7 additions & 5 deletions atlas-core/src/main/java/io/qameta/atlas/core/Atlas.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package io.qameta.atlas.core;

import io.qameta.atlas.core.api.Context;
import io.qameta.atlas.core.api.Listener;
import io.qameta.atlas.core.api.MethodExtension;
import io.qameta.atlas.core.api.MethodInvoker;
import io.qameta.atlas.core.api.Target;
import io.qameta.atlas.core.api.*;
import io.qameta.atlas.core.context.RetryerContext;
import io.qameta.atlas.core.context.TargetContext;
import io.qameta.atlas.core.internal.*;
import io.qameta.atlas.core.target.HardcodedTarget;
Expand All @@ -14,6 +11,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import static io.qameta.atlas.core.util.ReflectionUtils.getMethods;

Expand All @@ -30,6 +28,10 @@ public Atlas() {

public Atlas(final Configuration configuration) {
this.configuration = configuration;
final Optional<RetryerContext> context = this.configuration.getContext(RetryerContext.class);
if (!context.isPresent()) {
configuration.registerContext(new RetryerContext(new EmptyRetryer()));
}
}

public Atlas listener(final Listener listener) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*/
public interface Listener extends Extension {

void beforeMethodCall(MethodInfo methodInfo, Configuration configuration);
Configuration beforeMethodCall(MethodInfo methodInfo, Configuration configuration);

void afterMethodCall(MethodInfo methodInfo, Configuration configuration);

Expand Down
4 changes: 2 additions & 2 deletions atlas-core/src/main/java/io/qameta/atlas/core/api/Retry.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
@java.lang.annotation.Target(ElementType.METHOD)
public @interface Retry {

long timeout() default 5000L;
long timeout() default -1;

long polling() default 1000L;
long polling() default -1;

Class<? extends Throwable>[] ignoring() default {Throwable.class};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
package io.qameta.atlas.core.internal;

import io.qameta.atlas.core.api.MethodInvoker;
import io.qameta.atlas.core.api.Retry;
import io.qameta.atlas.core.api.Timeout;
import io.qameta.atlas.core.context.RetryerContext;
import io.qameta.atlas.core.util.MethodInfo;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;

/**
* Atlas method handler.
Expand All @@ -34,38 +32,41 @@ public AtlasMethodHandler(final Configuration configuration,
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
final MethodInfo methodInfo = new MethodInfo(method, args);

notifier.beforeMethodCall(methodInfo, configuration);
final Configuration runConfig = notifier.beforeMethodCall(methodInfo, this.configuration);
try {
final MethodInvoker handler = handlers.get(method);
final Object result = invokeWithRetry(handler, proxy, methodInfo);
notifier.onMethodReturn(methodInfo, configuration, result);
notifier.onMethodReturn(methodInfo, runConfig, result);
return result;
} catch (Throwable e) {
notifier.onMethodFailure(methodInfo, configuration, e);
notifier.onMethodFailure(methodInfo, runConfig, e);
throw e;
} finally {
notifier.afterMethodCall(methodInfo, configuration);
notifier.afterMethodCall(methodInfo, runConfig);
}
}

private Object invokeWithRetry(final MethodInvoker invoker,
final Object proxy,
final MethodInfo methodInfo) throws Throwable {
final Retryer retryer = Optional.ofNullable(methodInfo.getMethod().getAnnotation(Retry.class))
.map(retry -> (Retryer) new DefaultRetryer(retry.timeout(), retry.polling(),
Arrays.asList(retry.ignoring())))
.orElseGet(() -> configuration.getContext(RetryerContext.class)
.orElseGet(() -> new RetryerContext(new EmptyRetryer())).getValue());
methodInfo.getParameter(Integer.class, Timeout.class).ifPresent(retryer::timeoutInSeconds);
final Retryer retryer = getRetryer(configuration, methodInfo);
Throwable lastException;
do {
try {
return invoker.invoke(proxy, methodInfo, configuration);
} catch (Throwable e) {
lastException = e;
}
} while (retryer.shouldRetry(lastException));
} while (retryer.shouldRetry(lastException, methodInfo));
throw lastException;
}

private Retryer getRetryer(final Configuration configuration, final MethodInfo methodInfo) {
final Retryer retryer = configuration.requireContext(RetryerContext.class).getValue();
if (retryer instanceof TimeBasedRetryer) {
final Consumer<Integer> time = ((TimeBasedRetryer) retryer)::setTimeOutInSeconds;
methodInfo.getParameter(Integer.class, Timeout.class).ifPresent(time);
}
return retryer;
}
}
Original file line number Diff line number Diff line change
@@ -1,49 +1,27 @@
package io.qameta.atlas.core.internal;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.Set;


/**
* Retryer.
* @deprecated
* class constructor will be removed in the next release.
* Now the default implementation always used from the Atlas context
* see reference
*/
@SuppressWarnings("PMD.AvoidFieldNameMatchingMethodName")
public class DefaultRetryer implements Retryer {

private final List<Class<? extends Throwable>> ignoring;

private final Long start;

private Long timeout;

private Long polling;
@Deprecated
public class DefaultRetryer extends TimeBasedRetryer {

public DefaultRetryer(final Long timeout, final Long polling, final List<Class<? extends Throwable>> ignoring) {
this.ignoring = new ArrayList<>(ignoring);
this.start = System.currentTimeMillis();
this.timeout = timeout;
this.polling = polling;
}

public void ignore(final Class<? extends Throwable> throwable) {
this.ignoring.add(throwable);
}

public void timeoutInMillis(final Long millis) {
this.timeout = millis;
this(timeout, polling, new HashSet<>(ignoring));
}

@Override
public void timeoutInSeconds(final int seconds) {
this.timeout = TimeUnit.SECONDS.toMillis(seconds);
private DefaultRetryer(final Long timeout, final Long polling, final Set<Class<? extends Throwable>> ignoring) {
setTimeOut(timeout);
setPolling(polling);
addAllToIgnore(ignoring);
}

public void polling(final Long polling) {
this.polling = polling;
}

@Override
public boolean shouldRetry(final Throwable e) {
return shouldRetry(start, timeout, polling, ignoring, e);
}
}
Original file line number Diff line number Diff line change
@@ -1,35 +1,17 @@
package io.qameta.atlas.core.internal;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* Retryer with default values.
*/
public class EmptyRetryer implements Retryer {

private final Long start;
private final Long polling;
private final List<Class<? extends Throwable>> ignoring;
private Long timeout;
public class EmptyRetryer extends TimeBasedRetryer {

public EmptyRetryer() {
this.start = System.currentTimeMillis();
this.timeout = 5000L;
this.polling = 1000L;
this.ignoring = Collections.singletonList(Throwable.class);
}

@Override
public boolean shouldRetry(final Throwable e) {
return shouldRetry(start, timeout, polling, ignoring, e);

}

@Override
public void timeoutInSeconds(final int seconds) {
this.timeout = TimeUnit.SECONDS.toMillis(seconds);
setTimeOutInSeconds(5);
setPolling(1000);
addAllToIgnore(Stream.of(Throwable.class).collect(Collectors.toSet()));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,15 @@ public void addListeners(final Listener... listeners) {
}

@Override
public void beforeMethodCall(final MethodInfo methodInfo, final Configuration configuration) {
public Configuration beforeMethodCall(final MethodInfo methodInfo, final Configuration configuration) {
for (Listener listener : listeners) {
try {
listener.beforeMethodCall(methodInfo, configuration);
return listener.beforeMethodCall(methodInfo, configuration);
} catch (Exception e) {
LOGGER.error("Error during listener {} beforeMethodCall", listener, e);
}
}
return configuration;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,12 @@
package io.qameta.atlas.core.internal;

import java.util.List;
import io.qameta.atlas.core.util.MethodInfo;

/**
* Retryer.
*/
public interface Retryer {

boolean shouldRetry(Throwable e) throws Throwable;

default boolean shouldRetry(final Long start, final Long timeout, final Long polling,
final List<Class<? extends Throwable>> ignoring, final Throwable e) {
final long current = System.currentTimeMillis();
if (!(ignoring.stream().anyMatch(clazz -> clazz.isInstance(e)) && start + timeout < current)) {
try {
Thread.sleep(polling);
return true;
} catch (InterruptedException i) {
Thread.currentThread().interrupt();
}
}
return false;
}

void timeoutInSeconds(int seconds);
boolean shouldRetry(Throwable e, MethodInfo methodInfo) throws Throwable;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package io.qameta.atlas.core.internal;

import io.qameta.atlas.core.util.MethodInfo;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
* Default Retryer based on timeout.
*/
public abstract class TimeBasedRetryer implements Retryer {

private final Set<Class<? extends Throwable>> ignoring = new HashSet<>();
private final Long start = System.currentTimeMillis();
private Long timeout = 0L;
private Long polling = 0L;

@Override
public boolean shouldRetry(final Throwable e, final MethodInfo methodInfo) {
return shouldRetry(start, e);
}

public boolean shouldRetry(final Long start, final Throwable e) {
return shouldRetry(start, getTimeout(), getPolling(), getIgnoring(), e);
}

public boolean shouldRetry(final Long start, final Long timeout, final Long polling,
final Set<Class<? extends Throwable>> ignoring, final Throwable e) {
final long current = System.currentTimeMillis();
if (!(ignoring.stream().anyMatch(clazz -> clazz.isInstance(e)) && start + timeout < current)) {
try {
Thread.sleep(polling);
return true;
} catch (InterruptedException i) {
Thread.currentThread().interrupt();
}
}
return false;
}

public void setTimeOutInSeconds(final int seconds) {
setTimeOut(TimeUnit.SECONDS.toMillis(seconds));
}

public void setTimeOut(final long ms) {
this.timeout = ms;
}

public long getTimeout() {
return this.timeout;
}

public long getPolling() {
return polling;
}

public void setPolling(final long ms) {
this.polling = ms;
}

public Set<Class<? extends Throwable>> getIgnoring() {
return ignoring;
}

public void addAllToIgnore(final Set<Class<? extends Throwable>> toIgnoring) {
ignoring.addAll(toIgnoring);
}

}
Loading

0 comments on commit 1340d7e

Please sign in to comment.