New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Timeout Decorator #142
Merged
Merged
Timeout Decorator #142
Changes from 7 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
cb9a9a6
Add Timeout decorator.
maarek 6a3319e
Add cancelable config to cancel on exception.
maarek ba4be27
Remove TimeoutException:message constructor as it's unnecessary.
maarek ca088b2
Fix throwable chain.
maarek 82ae468
Fix issues to appease Codacy.
maarek a84955b
Update Timeout to impl only decorators for Future and Supplier<Future>.
maarek a245f39
Change Timeout to TimeLimiter.
maarek e798154
Documentation updates.
maarek File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
dependencies { | ||
compile ( libraries.rxjava2) | ||
} |
129 changes: 129 additions & 0 deletions
129
resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/TimeLimiter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
package io.github.resilience4j.timelimiter; | ||
|
||
import io.github.resilience4j.timelimiter.internal.TimeLimiterContext; | ||
|
||
import java.time.Duration; | ||
import java.util.concurrent.Callable; | ||
import java.util.concurrent.Future; | ||
import java.util.concurrent.TimeUnit; | ||
import java.util.concurrent.TimeoutException; | ||
import java.util.function.Supplier; | ||
|
||
/** | ||
* A TimeLimiter decorator stops execution at a configurable rate. | ||
*/ | ||
public interface TimeLimiter { | ||
|
||
/** | ||
* Creates a TimeLimiter decorator with a default TimeLimiterConfig configuration. | ||
* | ||
* @return The {@link TimeLimiter} | ||
*/ | ||
static TimeLimiter ofDefaults() { | ||
return new TimeLimiterContext(TimeLimiterConfig.ofDefaults()); | ||
} | ||
|
||
/** | ||
* Creates a TimeLimiter decorator with a TimeLimiterConfig configuration. | ||
* | ||
* @param timeLimiterConfig the TimeLimiterConfig | ||
* @return The {@link TimeLimiter} | ||
*/ | ||
static TimeLimiter of(TimeLimiterConfig timeLimiterConfig) { | ||
return new TimeLimiterContext(timeLimiterConfig); | ||
} | ||
|
||
/** | ||
* Creates a TimeLimiter decorator with a timeout Duration. | ||
* | ||
* @param timeoutDuration the timeout Duration | ||
* @return The {@link TimeLimiter} | ||
*/ | ||
static TimeLimiter of(Duration timeoutDuration) { | ||
TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom() | ||
.timeoutDuration(timeoutDuration) | ||
.build(); | ||
|
||
return new TimeLimiterContext(timeLimiterConfig); | ||
} | ||
|
||
/** | ||
* Creates a Callable which is restricted by a TimeLimiter. | ||
* | ||
* @param timeLimiter the {@link TimeLimiter} | ||
* @param future the original future | ||
* @param <T> the type of results supplied supplier | ||
* @param <F> the future type supplied | ||
* @return a future which is restricted by a {@link TimeLimiter}. | ||
*/ | ||
static <T, F extends Future<T>> Callable<T> decorateFuture(final TimeLimiter timeLimiter, final F future) { | ||
return () -> { | ||
try { | ||
return future.get(timeLimiter.getTimeLimiterConfig().getTimeoutDuration().toMillis(), TimeUnit.MILLISECONDS); | ||
} catch (TimeoutException e) { | ||
if (timeLimiter.getTimeLimiterConfig().shouldCancelRunningFuture()) { | ||
future.cancel(true); | ||
} | ||
throw e; | ||
} | ||
}; | ||
} | ||
|
||
/** | ||
* Creates a Callback which is restricted by a TimeLimiter. | ||
* | ||
* @param timeLimiter the {@link TimeLimiter} | ||
* @param futureSupplier the original future supplier | ||
* @param <T> the type of results supplied supplier | ||
* @param <F> the future type supplied | ||
* @return a future supplier which is restricted by a {@link TimeLimiter}. | ||
*/ | ||
static <T, F extends Future<T>> Callable<T> decorateFutureSupplier(TimeLimiter timeLimiter, Supplier<F> futureSupplier) { | ||
return () -> { | ||
Future<T> future = futureSupplier.get(); | ||
try { | ||
return future.get(timeLimiter.getTimeLimiterConfig().getTimeoutDuration().toMillis(), TimeUnit.MILLISECONDS); | ||
} catch (TimeoutException e) { | ||
if(timeLimiter.getTimeLimiterConfig().shouldCancelRunningFuture()){ | ||
future.cancel(true); | ||
} | ||
throw e; | ||
} | ||
}; | ||
} | ||
|
||
/** | ||
* Get the TimeLimiterConfig of this TimeLimiter decorator. | ||
* | ||
* @return the TimeLimiterConfig of this TimeLimiter decorator | ||
*/ | ||
TimeLimiterConfig getTimeLimiterConfig(); | ||
|
||
/** | ||
* Decorates and executes the decorated Future. | ||
* | ||
* @param future the original Future | ||
* | ||
* @return the result of the decorated Future. | ||
* @param <T> the result type of the future | ||
* @param <F> the type of Future | ||
* @throws Exception if unable to compute a result | ||
*/ | ||
default <T, F extends Future<T>> T executeFuture(F future) throws Exception { | ||
return decorateFuture(this, future).call(); | ||
} | ||
|
||
/** | ||
* Decorates and executes the decorated Future Supplier. | ||
* | ||
* @param futureSupplier the original future supplier | ||
* | ||
* @return the result of the Future. | ||
* @param <T> the result type of the future | ||
* @param <F> the type of Future | ||
* @throws Exception if unable to compute a result | ||
*/ | ||
default <T, F extends Future<T>> T executeFutureSupplier(Supplier<F> futureSupplier) throws Exception { | ||
return decorateFutureSupplier(this, futureSupplier).call(); | ||
} | ||
} |
91 changes: 91 additions & 0 deletions
91
...nce4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/TimeLimiterConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package io.github.resilience4j.timelimiter; | ||
|
||
import java.time.Duration; | ||
|
||
import static java.util.Objects.requireNonNull; | ||
|
||
public class TimeLimiterConfig { | ||
private static final String TIMEOUT_DURATION_MUST_NOT_BE_NULL = "TimeoutDuration must not be null"; | ||
|
||
private Duration timeoutDuration = Duration.ofSeconds(1); | ||
private boolean cancelRunningFuture = true; | ||
|
||
private TimeLimiterConfig() { | ||
} | ||
|
||
/** | ||
* Returns a builder to create a custom TimeLimiterConfig. | ||
* | ||
* @return a {@link TimeLimiterConfig.Builder} | ||
*/ | ||
public static Builder custom() { | ||
return new Builder(); | ||
} | ||
|
||
/** | ||
* Creates a default TimeLimiter configuration. | ||
* | ||
* @return a default TimeLimiter configuration. | ||
*/ | ||
public static TimeLimiterConfig ofDefaults(){ | ||
return new Builder().build(); | ||
} | ||
|
||
public Duration getTimeoutDuration() { | ||
return timeoutDuration; | ||
} | ||
|
||
public boolean shouldCancelRunningFuture() { | ||
return cancelRunningFuture; | ||
} | ||
|
||
@Override public String toString() { | ||
return "TimeLimiterConfig{" + | ||
"timeoutDuration=" + timeoutDuration + | ||
"cancelRunningFuture=" + cancelRunningFuture + | ||
'}'; | ||
} | ||
|
||
public static class Builder { | ||
|
||
private TimeLimiterConfig config = new TimeLimiterConfig(); | ||
|
||
/** | ||
* Builds a TimeLimiterConfig | ||
* | ||
* @return the TimeLimiterConfig | ||
*/ | ||
public TimeLimiterConfig build() { | ||
return config; | ||
} | ||
|
||
/** | ||
* Configures the thread execution timeout | ||
* Default value is 5 seconds. | ||
* | ||
* @param timeoutDuration the timeout Duration | ||
* @return the TimeLimiterConfig.Builder | ||
*/ | ||
public Builder timeoutDuration(final Duration timeoutDuration) { | ||
config.timeoutDuration = checkTimeoutDuration(timeoutDuration); | ||
return this; | ||
} | ||
|
||
/** | ||
* Configures whether cancel is called on the running future | ||
* Defaults to TRUE | ||
* | ||
* @param cancelRunningFuture to cancel or not | ||
* @return the TimeLimiterConfig.Builder | ||
*/ | ||
public Builder cancelRunningFuture(final boolean cancelRunningFuture) { | ||
config.cancelRunningFuture = cancelRunningFuture; | ||
return this; | ||
} | ||
|
||
} | ||
|
||
private static Duration checkTimeoutDuration(final Duration timeoutDuration) { | ||
return requireNonNull(timeoutDuration, TIMEOUT_DURATION_MUST_NOT_BE_NULL); | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
...limiter/src/main/java/io/github/resilience4j/timelimiter/internal/TimeLimiterContext.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package io.github.resilience4j.timelimiter.internal; | ||
|
||
import io.github.resilience4j.timelimiter.TimeLimiter; | ||
import io.github.resilience4j.timelimiter.TimeLimiterConfig; | ||
|
||
public class TimeLimiterContext implements TimeLimiter { | ||
private final TimeLimiterConfig timeLimiterConfig; | ||
|
||
public TimeLimiterContext(TimeLimiterConfig timeLimiterConfig) { | ||
this.timeLimiterConfig = timeLimiterConfig; | ||
} | ||
|
||
@Override | ||
public TimeLimiterConfig getTimeLimiterConfig() { | ||
return timeLimiterConfig; | ||
} | ||
} |
53 changes: 53 additions & 0 deletions
53
...j-timelimiter/src/test/java/io/github/resilience4j/timelimiter/TimeLimiterConfigTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package io.github.resilience4j.timelimiter; | ||
|
||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.junit.rules.ExpectedException; | ||
|
||
import java.time.Duration; | ||
|
||
import static org.assertj.core.api.BDDAssertions.then; | ||
|
||
public class TimeLimiterConfigTest { | ||
|
||
private static final Duration TIMEOUT = Duration.ofSeconds(5); | ||
private static final boolean SHOULD_CANCEL = false; | ||
private static final String TIMEOUT_DURATION_MUST_NOT_BE_NULL = "TimeoutDuration must not be null"; | ||
private static final String TIMEOUT_TO_STRING = "TimeLimiterConfig{timeoutDuration=PT1ScancelRunningFuture=true}"; | ||
|
||
@Rule | ||
public ExpectedException exception = ExpectedException.none(); | ||
|
||
|
||
@Test | ||
public void builderPositive() { | ||
TimeLimiterConfig config = TimeLimiterConfig.custom() | ||
.timeoutDuration(TIMEOUT) | ||
.cancelRunningFuture(SHOULD_CANCEL) | ||
.build(); | ||
|
||
then(config.getTimeoutDuration()).isEqualTo(TIMEOUT); | ||
then(config.shouldCancelRunningFuture()).isEqualTo(SHOULD_CANCEL); | ||
} | ||
|
||
@Test | ||
public void defaultConstruction() { | ||
TimeLimiterConfig config = TimeLimiterConfig.ofDefaults(); | ||
then(config.getTimeoutDuration()).isEqualTo(Duration.ofSeconds(1)); | ||
then(config.shouldCancelRunningFuture()).isTrue(); | ||
} | ||
|
||
@Test | ||
public void builderTimeoutIsNull() { | ||
exception.expect(NullPointerException.class); | ||
exception.expectMessage(TIMEOUT_DURATION_MUST_NOT_BE_NULL); | ||
|
||
TimeLimiterConfig.custom() | ||
.timeoutDuration(null); | ||
} | ||
|
||
@Test | ||
public void configToString() { | ||
then(TimeLimiterConfig.ofDefaults().toString()).isEqualTo(TIMEOUT_TO_STRING); | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
after a configurable duration, or?