From 6be60e8a5f5586f4fc69fd38fdab8048960494fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Skowro=C5=84ski?= <5780288+hexmind@users.noreply.github.com> Date: Fri, 23 Aug 2019 08:06:48 +0200 Subject: [PATCH] Issue #531: Added events to TimeLimiter (#583) --- resilience4j-timelimiter/build.gradle | 2 +- .../resilience4j/timelimiter/TimeLimiter.java | 70 +++++++----- .../timelimiter/TimeLimiterConfig.java | 7 +- .../event/AbstractTimeLimiterEvent.java | 58 ++++++++++ .../timelimiter/event/TimeLimiterEvent.java | 50 +++++++++ .../event/TimeLimiterOnFailureEvent.java | 27 +++++ .../event/TimeLimiterOnSuccessEvent.java | 27 +++++ .../event/TimeLimiterOnTimeoutEvent.java | 27 +++++ .../timelimiter/event/package-info.java | 24 +++++ .../internal/TimeLimiterEventProcessor.java | 54 ++++++++++ .../timelimiter/internal/TimeLimiterImpl.java | 71 +++++++++++- .../TimeLimiterEventPublisherTest.java | 101 ++++++++++++++++++ .../internal/TimeLimiterImplTest.java | 2 +- 13 files changed, 487 insertions(+), 33 deletions(-) create mode 100644 resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/event/AbstractTimeLimiterEvent.java create mode 100644 resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/event/TimeLimiterEvent.java create mode 100644 resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/event/TimeLimiterOnFailureEvent.java create mode 100644 resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/event/TimeLimiterOnSuccessEvent.java create mode 100644 resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/event/TimeLimiterOnTimeoutEvent.java create mode 100644 resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/event/package-info.java create mode 100644 resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/internal/TimeLimiterEventProcessor.java create mode 100644 resilience4j-timelimiter/src/test/java/io/github/resilience4j/timelimiter/TimeLimiterEventPublisherTest.java diff --git a/resilience4j-timelimiter/build.gradle b/resilience4j-timelimiter/build.gradle index 810fd39fa4..1f79cddec5 100644 --- a/resilience4j-timelimiter/build.gradle +++ b/resilience4j-timelimiter/build.gradle @@ -1,4 +1,4 @@ dependencies { - + compile project(':resilience4j-core') } ext.moduleName='io.github.resilience4j.timelimiter' \ No newline at end of file diff --git a/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/TimeLimiter.java b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/TimeLimiter.java index 87b02ed8d5..bba533ad5e 100644 --- a/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/TimeLimiter.java +++ b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/TimeLimiter.java @@ -1,9 +1,15 @@ package io.github.resilience4j.timelimiter; +import io.github.resilience4j.core.EventConsumer; +import io.github.resilience4j.timelimiter.event.TimeLimiterEvent; +import io.github.resilience4j.timelimiter.event.TimeLimiterOnFailureEvent; +import io.github.resilience4j.timelimiter.event.TimeLimiterOnSuccessEvent; +import io.github.resilience4j.timelimiter.event.TimeLimiterOnTimeoutEvent; import io.github.resilience4j.timelimiter.internal.TimeLimiterImpl; import java.time.Duration; -import java.util.concurrent.*; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; import java.util.function.Supplier; /** @@ -11,13 +17,15 @@ */ public interface TimeLimiter { + String DEFAULT_NAME = "UNDEFINED"; + /** * Creates a TimeLimiter decorator with a default TimeLimiterConfig configuration. * * @return The {@link TimeLimiter} */ static TimeLimiter ofDefaults() { - return new TimeLimiterImpl(TimeLimiterConfig.ofDefaults()); + return new TimeLimiterImpl(DEFAULT_NAME, TimeLimiterConfig.ofDefaults()); } /** @@ -27,7 +35,18 @@ static TimeLimiter ofDefaults() { * @return The {@link TimeLimiter} */ static TimeLimiter of(TimeLimiterConfig timeLimiterConfig) { - return new TimeLimiterImpl(timeLimiterConfig); + return of(DEFAULT_NAME, timeLimiterConfig); + } + + /** + * Creates a TimeLimiter decorator with a TimeLimiterConfig configuration. + * + * @param name the name of the TimeLimiter + * @param timeLimiterConfig the TimeLimiterConfig + * @return The {@link TimeLimiter} + */ + static TimeLimiter of(String name, TimeLimiterConfig timeLimiterConfig) { + return new TimeLimiterImpl(name, timeLimiterConfig); } /** @@ -40,8 +59,7 @@ static TimeLimiter of(Duration timeoutDuration) { TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom() .timeoutDuration(timeoutDuration) .build(); - - return new TimeLimiterImpl(timeLimiterConfig); + return new TimeLimiterImpl(DEFAULT_NAME, timeLimiterConfig); } /** @@ -54,26 +72,7 @@ static TimeLimiter of(Duration timeoutDuration) { * @return a future supplier which is restricted by a {@link TimeLimiter}. */ static > Callable decorateFutureSupplier(TimeLimiter timeLimiter, Supplier futureSupplier) { - return () -> { - Future 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; - } catch (ExecutionException e){ - Throwable t = e.getCause(); - if (t == null){ - throw e; - } - if (t instanceof Error){ - throw (Error) t; - } - throw (Exception) t; - } - }; + return timeLimiter.decorateFutureSupplier(futureSupplier); } /** @@ -104,7 +103,24 @@ default > T executeFutureSupplier(Supplier futureSuppl * @param the future type supplied * @return a future supplier which is restricted by a {@link TimeLimiter}. */ - default > Callable decorateFutureSupplier(Supplier futureSupplier) { - return decorateFutureSupplier(this, futureSupplier); + > Callable decorateFutureSupplier(Supplier futureSupplier); + + EventPublisher getEventPublisher(); + + void onSuccess(); + + void onError(Exception exception); + + /** + * An EventPublisher which can be used to register event consumers. + */ + interface EventPublisher extends io.github.resilience4j.core.EventPublisher { + + EventPublisher onSuccess(EventConsumer eventConsumer); + + EventPublisher onFailure(EventConsumer eventConsumer); + + EventPublisher onTimeout(EventConsumer eventConsumer); + } } diff --git a/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/TimeLimiterConfig.java b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/TimeLimiterConfig.java index 95321ced03..4ef7411191 100644 --- a/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/TimeLimiterConfig.java +++ b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/TimeLimiterConfig.java @@ -7,7 +7,7 @@ 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 Duration timeoutDuration = Duration.ofSeconds(1); private boolean cancelRunningFuture = true; private TimeLimiterConfig() { @@ -27,7 +27,7 @@ public static Builder custom() { * * @return a default TimeLimiter configuration. */ - public static TimeLimiterConfig ofDefaults(){ + public static TimeLimiterConfig ofDefaults() { return new Builder().build(); } @@ -39,7 +39,8 @@ public boolean shouldCancelRunningFuture() { return cancelRunningFuture; } - @Override public String toString() { + @Override + public String toString() { return "TimeLimiterConfig{" + "timeoutDuration=" + timeoutDuration + "cancelRunningFuture=" + cancelRunningFuture + diff --git a/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/event/AbstractTimeLimiterEvent.java b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/event/AbstractTimeLimiterEvent.java new file mode 100644 index 0000000000..f5fa2f50e0 --- /dev/null +++ b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/event/AbstractTimeLimiterEvent.java @@ -0,0 +1,58 @@ +/* + * + * Copyright 2019 authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + */ +package io.github.resilience4j.timelimiter.event; + +import java.time.ZonedDateTime; + +public abstract class AbstractTimeLimiterEvent implements TimeLimiterEvent { + + private final String timeLimiterName; + private Type eventType; + private final ZonedDateTime creationTime; + + AbstractTimeLimiterEvent(String timeLimiterName, Type eventType) { + this.timeLimiterName = timeLimiterName; + this.eventType = eventType; + this.creationTime = ZonedDateTime.now(); + } + + @Override + public String getTimeLimiterName() { + return timeLimiterName; + } + + @Override + public ZonedDateTime getCreationTime() { + return creationTime; + } + + @Override + public Type getEventType() { + return eventType; + } + + @Override + public String toString() { + return "TimeLimiterEvent{" + + "type=" + getEventType() + + ", timeLimiterName='" + getTimeLimiterName() + '\'' + + ", creationTime=" + getCreationTime() + + '}'; + } +} diff --git a/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/event/TimeLimiterEvent.java b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/event/TimeLimiterEvent.java new file mode 100644 index 0000000000..d33c213190 --- /dev/null +++ b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/event/TimeLimiterEvent.java @@ -0,0 +1,50 @@ +/* + * + * Copyright 2019 authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + */ +package io.github.resilience4j.timelimiter.event; + +import java.time.ZonedDateTime; + +/** + * An event which is created by a {@link io.github.resilience4j.timelimiter.TimeLimiter}. + */ +public interface TimeLimiterEvent { + + static TimeLimiterEvent of(String name, Type eventType) { + switch (eventType) { + case SUCCESS: + return new TimeLimiterOnSuccessEvent(name); + case TIMEOUT: + return new TimeLimiterOnTimeoutEvent(name); + default: + return new TimeLimiterOnFailureEvent(name); + } + } + + String getTimeLimiterName(); + + Type getEventType(); + + ZonedDateTime getCreationTime(); + + enum Type { + SUCCESS, + TIMEOUT, + FAILURE + } +} diff --git a/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/event/TimeLimiterOnFailureEvent.java b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/event/TimeLimiterOnFailureEvent.java new file mode 100644 index 0000000000..c531071998 --- /dev/null +++ b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/event/TimeLimiterOnFailureEvent.java @@ -0,0 +1,27 @@ +/* + * + * Copyright 2019 authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + */ +package io.github.resilience4j.timelimiter.event; + +public class TimeLimiterOnFailureEvent extends AbstractTimeLimiterEvent { + + public TimeLimiterOnFailureEvent(String timeLimiterName) { + super(timeLimiterName, Type.FAILURE); + } + +} diff --git a/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/event/TimeLimiterOnSuccessEvent.java b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/event/TimeLimiterOnSuccessEvent.java new file mode 100644 index 0000000000..c7078d0b02 --- /dev/null +++ b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/event/TimeLimiterOnSuccessEvent.java @@ -0,0 +1,27 @@ +/* + * + * Copyright 2019 authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + */ +package io.github.resilience4j.timelimiter.event; + +public class TimeLimiterOnSuccessEvent extends AbstractTimeLimiterEvent { + + public TimeLimiterOnSuccessEvent(String timeLimiterName) { + super(timeLimiterName, Type.SUCCESS); + } + +} diff --git a/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/event/TimeLimiterOnTimeoutEvent.java b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/event/TimeLimiterOnTimeoutEvent.java new file mode 100644 index 0000000000..785ee9f311 --- /dev/null +++ b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/event/TimeLimiterOnTimeoutEvent.java @@ -0,0 +1,27 @@ +/* + * + * Copyright 2019 authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + */ +package io.github.resilience4j.timelimiter.event; + +public class TimeLimiterOnTimeoutEvent extends AbstractTimeLimiterEvent { + + public TimeLimiterOnTimeoutEvent(String timeLimiterName) { + super(timeLimiterName, Type.TIMEOUT); + } + +} diff --git a/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/event/package-info.java b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/event/package-info.java new file mode 100644 index 0000000000..317f793930 --- /dev/null +++ b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/event/package-info.java @@ -0,0 +1,24 @@ +/* + * + * Copyright 2019 authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + */ +@NonNullApi +@NonNullFields +package io.github.resilience4j.timelimiter.event; + +import io.github.resilience4j.core.lang.NonNullApi; +import io.github.resilience4j.core.lang.NonNullFields; \ No newline at end of file diff --git a/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/internal/TimeLimiterEventProcessor.java b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/internal/TimeLimiterEventProcessor.java new file mode 100644 index 0000000000..7aab3d7abb --- /dev/null +++ b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/internal/TimeLimiterEventProcessor.java @@ -0,0 +1,54 @@ +/* + * + * Copyright 2019 authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + */ +package io.github.resilience4j.timelimiter.internal; + + +import io.github.resilience4j.core.EventConsumer; +import io.github.resilience4j.core.EventProcessor; +import io.github.resilience4j.timelimiter.TimeLimiter; +import io.github.resilience4j.timelimiter.event.TimeLimiterEvent; +import io.github.resilience4j.timelimiter.event.TimeLimiterOnFailureEvent; +import io.github.resilience4j.timelimiter.event.TimeLimiterOnSuccessEvent; +import io.github.resilience4j.timelimiter.event.TimeLimiterOnTimeoutEvent; + +public class TimeLimiterEventProcessor extends EventProcessor implements EventConsumer, TimeLimiter.EventPublisher { + + @Override + public void consumeEvent(TimeLimiterEvent event) { + super.processEvent(event); + } + + @Override + public TimeLimiter.EventPublisher onSuccess(EventConsumer onSuccessEventConsumer) { + registerConsumer(TimeLimiterOnSuccessEvent.class.getSimpleName(), onSuccessEventConsumer); + return this; + } + + @Override + public TimeLimiter.EventPublisher onFailure(EventConsumer onOnFailureEventConsumer) { + registerConsumer(TimeLimiterOnFailureEvent.class.getSimpleName(), onOnFailureEventConsumer); + return this; + } + + @Override + public TimeLimiter.EventPublisher onTimeout(EventConsumer onOnFailureEventConsumer) { + registerConsumer(TimeLimiterOnTimeoutEvent.class.getSimpleName(), onOnFailureEventConsumer); + return this; + } +} \ No newline at end of file diff --git a/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/internal/TimeLimiterImpl.java b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/internal/TimeLimiterImpl.java index fe6cf08db8..c7ac0e6c2f 100644 --- a/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/internal/TimeLimiterImpl.java +++ b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/internal/TimeLimiterImpl.java @@ -2,16 +2,85 @@ import io.github.resilience4j.timelimiter.TimeLimiter; import io.github.resilience4j.timelimiter.TimeLimiterConfig; +import io.github.resilience4j.timelimiter.event.TimeLimiterEvent; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Supplier; public class TimeLimiterImpl implements TimeLimiter { + + private String name; private final TimeLimiterConfig timeLimiterConfig; + private final TimeLimiterEventProcessor eventProcessor; - public TimeLimiterImpl(TimeLimiterConfig timeLimiterConfig) { + public TimeLimiterImpl(String name, TimeLimiterConfig timeLimiterConfig) { + this.name = name; this.timeLimiterConfig = timeLimiterConfig; + this.eventProcessor = new TimeLimiterEventProcessor(); + } + + @Override + public > Callable decorateFutureSupplier(Supplier futureSupplier) { + return () -> { + Future future = futureSupplier.get(); + try { + T result = future.get(getTimeLimiterConfig().getTimeoutDuration().toMillis(), TimeUnit.MILLISECONDS); + onSuccess(); + return result; + } catch (TimeoutException e) { + onError(e); + if (getTimeLimiterConfig().shouldCancelRunningFuture()) { + future.cancel(true); + } + throw e; + } catch (ExecutionException e) { + onError(e); + Throwable t = e.getCause(); + if (t == null) { + throw e; + } + if (t instanceof Error) { + throw (Error) t; + } + throw (Exception) t; + } + }; + } + + private void publishTimeLimiterEvent(TimeLimiterEvent.Type eventType) { + if (!eventProcessor.hasConsumers()) { + return; + } + eventProcessor.consumeEvent(TimeLimiterEvent.of(name, eventType)); + } @Override public TimeLimiterConfig getTimeLimiterConfig() { return timeLimiterConfig; } + + @Override + public EventPublisher getEventPublisher() { + return eventProcessor; + } + + @Override + public void onSuccess() { + publishTimeLimiterEvent(TimeLimiterEvent.Type.SUCCESS); + } + + @Override + public void onError(Exception e) { + if (e instanceof TimeoutException) { + publishTimeLimiterEvent(TimeLimiterEvent.Type.TIMEOUT); + } else { + publishTimeLimiterEvent(TimeLimiterEvent.Type.FAILURE); + } + } + } diff --git a/resilience4j-timelimiter/src/test/java/io/github/resilience4j/timelimiter/TimeLimiterEventPublisherTest.java b/resilience4j-timelimiter/src/test/java/io/github/resilience4j/timelimiter/TimeLimiterEventPublisherTest.java new file mode 100644 index 0000000000..739861b7ff --- /dev/null +++ b/resilience4j-timelimiter/src/test/java/io/github/resilience4j/timelimiter/TimeLimiterEventPublisherTest.java @@ -0,0 +1,101 @@ +/* + * + * Copyright 2019 authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + */ +package io.github.resilience4j.timelimiter; + +import io.github.resilience4j.timelimiter.event.TimeLimiterEvent; +import io.vavr.control.Try; + +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +import org.junit.Test; +import org.slf4j.Logger; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; + + +public class TimeLimiterEventPublisherTest { + + private static final Duration NEVER = Duration.ZERO; + + private Logger logger = mock(Logger.class); + + @Test + public void shouldReturnTheSameConsumer() { + TimeLimiter timeLimiter = TimeLimiter.of(NEVER); + + TimeLimiter.EventPublisher eventPublisher = timeLimiter.getEventPublisher(); + TimeLimiter.EventPublisher eventPublisher2 = timeLimiter.getEventPublisher(); + + assertThat(eventPublisher).isEqualTo(eventPublisher2); + } + + @Test + public void shouldConsumeOnSuccessEvent() throws Exception { + TimeLimiter timeLimiter = TimeLimiter.of(NEVER); + timeLimiter.getEventPublisher() + .onSuccess(this::logEventType); + Supplier> futureSupplier = () -> + CompletableFuture.completedFuture("Hello world"); + + String result = timeLimiter.decorateFutureSupplier(futureSupplier).call(); + + assertThat(result).isEqualTo("Hello world"); + then(logger).should(times(1)).info("SUCCESS"); + } + + @Test + public void shouldConsumeOnTimeoutEvent() { + TimeLimiter timeLimiter = TimeLimiter.of(NEVER); + timeLimiter.getEventPublisher() + .onTimeout(this::logEventType); + Supplier> futureSupplier = () -> + CompletableFuture.supplyAsync(this::fail); + + Try.ofCallable(timeLimiter.decorateFutureSupplier(futureSupplier)); + + then(logger).should(times(1)).info("TIMEOUT"); + } + + @Test + public void shouldConsumeOnFailureEvent() { + TimeLimiter timeLimiter = TimeLimiter.of(Duration.ofSeconds(1)); + timeLimiter.getEventPublisher() + .onFailure(this::logEventType); + Supplier> futureSupplier = () -> + CompletableFuture.supplyAsync(this::fail); + + Try.ofCallable(timeLimiter.decorateFutureSupplier(futureSupplier)); + + then(logger).should(times(1)).info("FAILURE"); + } + + private void logEventType(TimeLimiterEvent event) { + logger.info(event.getEventType().toString()); + } + + private String fail() { + throw new RuntimeException(); + } + +} \ No newline at end of file diff --git a/resilience4j-timelimiter/src/test/java/io/github/resilience4j/timelimiter/internal/TimeLimiterImplTest.java b/resilience4j-timelimiter/src/test/java/io/github/resilience4j/timelimiter/internal/TimeLimiterImplTest.java index 11cae77014..0d27a15dc1 100644 --- a/resilience4j-timelimiter/src/test/java/io/github/resilience4j/timelimiter/internal/TimeLimiterImplTest.java +++ b/resilience4j-timelimiter/src/test/java/io/github/resilience4j/timelimiter/internal/TimeLimiterImplTest.java @@ -24,7 +24,7 @@ public void init() { timeLimiterConfig = TimeLimiterConfig.custom() .timeoutDuration(Duration.ZERO) .build(); - TimeLimiterImpl testTimeout = new TimeLimiterImpl(timeLimiterConfig); + TimeLimiterImpl testTimeout = new TimeLimiterImpl("name", timeLimiterConfig); timeout = PowerMockito.spy(testTimeout); }