diff --git a/resilience4j-bulkhead/src/main/java/io/github/resilience4j/bulkhead/internal/FixedThreadPoolBulkhead.java b/resilience4j-bulkhead/src/main/java/io/github/resilience4j/bulkhead/internal/FixedThreadPoolBulkhead.java index 399bb7ef4a..091df41cf2 100644 --- a/resilience4j-bulkhead/src/main/java/io/github/resilience4j/bulkhead/internal/FixedThreadPoolBulkhead.java +++ b/resilience4j-bulkhead/src/main/java/io/github/resilience4j/bulkhead/internal/FixedThreadPoolBulkhead.java @@ -204,19 +204,19 @@ private class BulkheadEventProcessor extends EventProcessor imple @Override public ThreadPoolBulkheadEventPublisher onCallPermitted(EventConsumer onCallPermittedEventConsumer) { - registerConsumer(BulkheadOnCallPermittedEvent.class, onCallPermittedEventConsumer); + registerConsumer(BulkheadOnCallPermittedEvent.class.getSimpleName(), onCallPermittedEventConsumer); return this; } @Override public ThreadPoolBulkheadEventPublisher onCallRejected(EventConsumer onCallRejectedEventConsumer) { - registerConsumer(BulkheadOnCallRejectedEvent.class, onCallRejectedEventConsumer); + registerConsumer(BulkheadOnCallRejectedEvent.class.getSimpleName(), onCallRejectedEventConsumer); return this; } @Override public ThreadPoolBulkheadEventPublisher onCallFinished(EventConsumer onCallFinishedEventConsumer) { - registerConsumer(BulkheadOnCallFinishedEvent.class, onCallFinishedEventConsumer); + registerConsumer(BulkheadOnCallFinishedEvent.class.getSimpleName(), onCallFinishedEventConsumer); return this; } diff --git a/resilience4j-bulkhead/src/main/java/io/github/resilience4j/bulkhead/internal/InMemoryBulkheadRegistry.java b/resilience4j-bulkhead/src/main/java/io/github/resilience4j/bulkhead/internal/InMemoryBulkheadRegistry.java index 25b834d9af..33ff98f8ee 100644 --- a/resilience4j-bulkhead/src/main/java/io/github/resilience4j/bulkhead/internal/InMemoryBulkheadRegistry.java +++ b/resilience4j-bulkhead/src/main/java/io/github/resilience4j/bulkhead/internal/InMemoryBulkheadRegistry.java @@ -21,7 +21,7 @@ import io.github.resilience4j.bulkhead.Bulkhead; import io.github.resilience4j.bulkhead.BulkheadConfig; import io.github.resilience4j.bulkhead.BulkheadRegistry; -import io.github.resilience4j.core.AbstractRegistry; +import io.github.resilience4j.core.registry.AbstractRegistry; import io.github.resilience4j.core.ConfigurationNotFoundException; import io.vavr.collection.Array; import io.vavr.collection.Seq; @@ -62,7 +62,7 @@ public InMemoryBulkheadRegistry(BulkheadConfig defaultConfig) { */ @Override public Seq getAllBulkheads() { - return Array.ofAll(targetMap.values()); + return Array.ofAll(entryMap.values()); } /** diff --git a/resilience4j-bulkhead/src/main/java/io/github/resilience4j/bulkhead/internal/InMemoryThreadPoolBulkheadRegistry.java b/resilience4j-bulkhead/src/main/java/io/github/resilience4j/bulkhead/internal/InMemoryThreadPoolBulkheadRegistry.java index 9457cab3d5..fdee434cbd 100644 --- a/resilience4j-bulkhead/src/main/java/io/github/resilience4j/bulkhead/internal/InMemoryThreadPoolBulkheadRegistry.java +++ b/resilience4j-bulkhead/src/main/java/io/github/resilience4j/bulkhead/internal/InMemoryThreadPoolBulkheadRegistry.java @@ -21,7 +21,7 @@ import io.github.resilience4j.bulkhead.ThreadPoolBulkhead; import io.github.resilience4j.bulkhead.ThreadPoolBulkheadConfig; import io.github.resilience4j.bulkhead.ThreadPoolBulkheadRegistry; -import io.github.resilience4j.core.AbstractRegistry; +import io.github.resilience4j.core.registry.AbstractRegistry; import io.github.resilience4j.core.ConfigurationNotFoundException; import io.vavr.collection.Array; import io.vavr.collection.Seq; @@ -62,7 +62,7 @@ public InMemoryThreadPoolBulkheadRegistry(ThreadPoolBulkheadConfig defaultConfig */ @Override public Seq getAllBulkheads() { - return Array.ofAll(targetMap.values()); + return Array.ofAll(entryMap.values()); } /** diff --git a/resilience4j-bulkhead/src/main/java/io/github/resilience4j/bulkhead/internal/SemaphoreBulkhead.java b/resilience4j-bulkhead/src/main/java/io/github/resilience4j/bulkhead/internal/SemaphoreBulkhead.java index 1410ebc42e..56e04132e7 100644 --- a/resilience4j-bulkhead/src/main/java/io/github/resilience4j/bulkhead/internal/SemaphoreBulkhead.java +++ b/resilience4j-bulkhead/src/main/java/io/github/resilience4j/bulkhead/internal/SemaphoreBulkhead.java @@ -173,19 +173,19 @@ private class BulkheadEventProcessor extends EventProcessor imple @Override public EventPublisher onCallPermitted(EventConsumer onCallPermittedEventConsumer) { - registerConsumer(BulkheadOnCallPermittedEvent.class, onCallPermittedEventConsumer); + registerConsumer(BulkheadOnCallPermittedEvent.class.getSimpleName(), onCallPermittedEventConsumer); return this; } @Override public EventPublisher onCallRejected(EventConsumer onCallRejectedEventConsumer) { - registerConsumer(BulkheadOnCallRejectedEvent.class, onCallRejectedEventConsumer); + registerConsumer(BulkheadOnCallRejectedEvent.class.getSimpleName(), onCallRejectedEventConsumer); return this; } @Override public EventPublisher onCallFinished(EventConsumer onCallFinishedEventConsumer) { - registerConsumer(BulkheadOnCallFinishedEvent.class, onCallFinishedEventConsumer); + registerConsumer(BulkheadOnCallFinishedEvent.class.getSimpleName(), onCallFinishedEventConsumer); return this; } diff --git a/resilience4j-bulkhead/src/test/java/io/github/resilience4j/bulkhead/BulkheadRegistryTest.java b/resilience4j-bulkhead/src/test/java/io/github/resilience4j/bulkhead/BulkheadRegistryTest.java index fc0405bf51..7a78cbbfb1 100644 --- a/resilience4j-bulkhead/src/test/java/io/github/resilience4j/bulkhead/BulkheadRegistryTest.java +++ b/resilience4j-bulkhead/src/test/java/io/github/resilience4j/bulkhead/BulkheadRegistryTest.java @@ -20,16 +20,13 @@ import org.junit.Before; import org.junit.Test; -import org.mockito.BDDMockito; import org.slf4j.Logger; import java.util.HashMap; import java.util.Map; -import java.util.function.Consumer; import static org.assertj.core.api.BDDAssertions.assertThat; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; public class BulkheadRegistryTest { @@ -37,14 +34,12 @@ public class BulkheadRegistryTest { private BulkheadConfig config; private BulkheadRegistry registry; private Logger LOGGER; - private Consumer post_consumer = circuitBreaker -> LOGGER.info("invoking the post consumer1"); @Before public void setUp() { LOGGER = mock(Logger.class); // registry with default config registry = BulkheadRegistry.ofDefaults(); - registry.registerPostCreationConsumer(post_consumer); // registry with custom config config = BulkheadConfig.custom() .maxConcurrentCalls(100) @@ -71,7 +66,6 @@ public void shouldReturnTheCorrectName() { assertThat(bulkhead.getName()).isEqualTo("test"); assertThat(bulkhead.getBulkheadConfig().getMaxConcurrentCalls()).isEqualTo(25); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(25); - BDDMockito.then(LOGGER).should(times(1)).info("invoking the post consumer1"); } @Test @@ -82,7 +76,6 @@ public void shouldBeTheSameInstance() { assertThat(bulkhead1).isSameAs(bulkhead2); assertThat(registry.getAllBulkheads()).hasSize(1); - BDDMockito.then(LOGGER).should(times(1)).info("invoking the post consumer1"); } @Test @@ -93,7 +86,6 @@ public void shouldBeNotTheSameInstance() { assertThat(bulkhead1).isNotSameAs(bulkhead2); assertThat(registry.getAllBulkheads()).hasSize(2); - BDDMockito.then(LOGGER).should(times(2)).info("invoking the post consumer1"); } @Test diff --git a/resilience4j-cache/src/main/java/io/github/resilience4j/cache/internal/CacheImpl.java b/resilience4j-cache/src/main/java/io/github/resilience4j/cache/internal/CacheImpl.java index bd9719b08f..9d6a9a9ace 100644 --- a/resilience4j-cache/src/main/java/io/github/resilience4j/cache/internal/CacheImpl.java +++ b/resilience4j-cache/src/main/java/io/github/resilience4j/cache/internal/CacheImpl.java @@ -127,19 +127,19 @@ private class CacheEventProcessor extends EventProcessor implements @Override public EventPublisher onCacheHit(EventConsumer eventConsumer) { - registerConsumer(CacheOnHitEvent.class, eventConsumer); + registerConsumer(CacheOnHitEvent.class.getSimpleName(), eventConsumer); return this; } @Override public EventPublisher onCacheMiss(EventConsumer eventConsumer) { - registerConsumer(CacheOnMissEvent.class, eventConsumer); + registerConsumer(CacheOnMissEvent.class.getSimpleName(), eventConsumer); return this; } @Override public EventPublisher onError(EventConsumer eventConsumer) { - registerConsumer(CacheOnErrorEvent.class, eventConsumer); + registerConsumer(CacheOnErrorEvent.class.getSimpleName(), eventConsumer); return this; } diff --git a/resilience4j-circuitbreaker/src/main/java/io/github/resilience4j/circuitbreaker/internal/CircuitBreakerStateMachine.java b/resilience4j-circuitbreaker/src/main/java/io/github/resilience4j/circuitbreaker/internal/CircuitBreakerStateMachine.java index f46ff8da2b..93d291cca9 100644 --- a/resilience4j-circuitbreaker/src/main/java/io/github/resilience4j/circuitbreaker/internal/CircuitBreakerStateMachine.java +++ b/resilience4j-circuitbreaker/src/main/java/io/github/resilience4j/circuitbreaker/internal/CircuitBreakerStateMachine.java @@ -333,37 +333,37 @@ Clock getClock() { private class CircuitBreakerEventProcessor extends EventProcessor implements EventConsumer, EventPublisher { @Override public EventPublisher onSuccess(EventConsumer onSuccessEventConsumer) { - registerConsumer(CircuitBreakerOnSuccessEvent.class, onSuccessEventConsumer); + registerConsumer(CircuitBreakerOnSuccessEvent.class.getSimpleName(), onSuccessEventConsumer); return this; } @Override public EventPublisher onError(EventConsumer onErrorEventConsumer) { - registerConsumer(CircuitBreakerOnErrorEvent.class, onErrorEventConsumer); + registerConsumer(CircuitBreakerOnErrorEvent.class.getSimpleName(), onErrorEventConsumer); return this; } @Override public EventPublisher onStateTransition(EventConsumer onStateTransitionEventConsumer) { - registerConsumer(CircuitBreakerOnStateTransitionEvent.class, onStateTransitionEventConsumer); + registerConsumer(CircuitBreakerOnStateTransitionEvent.class.getSimpleName(), onStateTransitionEventConsumer); return this; } @Override public EventPublisher onReset(EventConsumer onResetEventConsumer) { - registerConsumer(CircuitBreakerOnResetEvent.class, onResetEventConsumer); + registerConsumer(CircuitBreakerOnResetEvent.class.getSimpleName(), onResetEventConsumer); return this; } @Override public EventPublisher onIgnoredError(EventConsumer onIgnoredErrorEventConsumer) { - registerConsumer(CircuitBreakerOnIgnoredErrorEvent.class, onIgnoredErrorEventConsumer); + registerConsumer(CircuitBreakerOnIgnoredErrorEvent.class.getSimpleName(), onIgnoredErrorEventConsumer); return this; } @Override public EventPublisher onCallNotPermitted(EventConsumer onCallNotPermittedEventConsumer) { - registerConsumer(CircuitBreakerOnCallNotPermittedEvent.class, onCallNotPermittedEventConsumer); + registerConsumer(CircuitBreakerOnCallNotPermittedEvent.class.getSimpleName(), onCallNotPermittedEventConsumer); return this; } diff --git a/resilience4j-circuitbreaker/src/main/java/io/github/resilience4j/circuitbreaker/internal/InMemoryCircuitBreakerRegistry.java b/resilience4j-circuitbreaker/src/main/java/io/github/resilience4j/circuitbreaker/internal/InMemoryCircuitBreakerRegistry.java index 23bf6be321..cf5becbea5 100644 --- a/resilience4j-circuitbreaker/src/main/java/io/github/resilience4j/circuitbreaker/internal/InMemoryCircuitBreakerRegistry.java +++ b/resilience4j-circuitbreaker/src/main/java/io/github/resilience4j/circuitbreaker/internal/InMemoryCircuitBreakerRegistry.java @@ -21,7 +21,7 @@ import io.github.resilience4j.circuitbreaker.CircuitBreaker; import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; -import io.github.resilience4j.core.AbstractRegistry; +import io.github.resilience4j.core.registry.AbstractRegistry; import io.github.resilience4j.core.ConfigurationNotFoundException; import io.vavr.collection.Array; import io.vavr.collection.Seq; @@ -62,7 +62,7 @@ public InMemoryCircuitBreakerRegistry(CircuitBreakerConfig defaultConfig) { */ @Override public Seq getAllCircuitBreakers() { - return Array.ofAll(targetMap.values()); + return Array.ofAll(entryMap.values()); } /** diff --git a/resilience4j-circuitbreaker/src/test/java/io/github/resilience4j/circuitbreaker/internal/InMemoryCircuitBreakerRegistryTest.java b/resilience4j-circuitbreaker/src/test/java/io/github/resilience4j/circuitbreaker/internal/InMemoryCircuitBreakerRegistryTest.java index f9b5562074..ab6e2805cc 100644 --- a/resilience4j-circuitbreaker/src/test/java/io/github/resilience4j/circuitbreaker/internal/InMemoryCircuitBreakerRegistryTest.java +++ b/resilience4j-circuitbreaker/src/test/java/io/github/resilience4j/circuitbreaker/internal/InMemoryCircuitBreakerRegistryTest.java @@ -1,23 +1,19 @@ package io.github.resilience4j.circuitbreaker.internal; -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.mockito.BDDMockito.then; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.Consumer; - +import io.github.resilience4j.circuitbreaker.CircuitBreaker; +import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; +import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; +import io.github.resilience4j.core.ConfigurationNotFoundException; import org.assertj.core.api.Assertions; import org.junit.Before; import org.junit.Test; import org.slf4j.Logger; -import io.github.resilience4j.circuitbreaker.CircuitBreaker; -import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; -import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; -import io.github.resilience4j.core.ConfigurationNotFoundException; +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.Mockito.mock; public class InMemoryCircuitBreakerRegistryTest { @@ -29,26 +25,6 @@ public void setUp() { LOGGER = mock(Logger.class); } - @Test - public void testPostConsumerBeingCalled() { - CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults(); - Consumer consumer1 = circuitBreaker -> LOGGER.info("invoking the post consumer1"); - Consumer consumer2 = circuitBreaker -> LOGGER.info("invoking the post consumer2"); - - circuitBreakerRegistry.registerPostCreationConsumer(consumer1); - - circuitBreakerRegistry.circuitBreaker("testCircuitBreaker"); - circuitBreakerRegistry.circuitBreaker("testCircuitBreaker2", CircuitBreakerConfig.ofDefaults()); - circuitBreakerRegistry.circuitBreaker("testCircuitBreaker3", CircuitBreakerConfig::ofDefaults); - - then(LOGGER).should(times(3)).info("invoking the post consumer1"); - - circuitBreakerRegistry.registerPostCreationConsumer(consumer2); - circuitBreakerRegistry.unregisterPostCreationConsumer(consumer1); - circuitBreakerRegistry.circuitBreaker("testCircuitBreaker4"); - then(LOGGER).should(times(1)).info("invoking the post consumer2"); - } - @Test public void testAddCircuitBreakerRegistry() { CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults(); diff --git a/resilience4j-core/src/main/java/io/github/resilience4j/core/EventProcessor.java b/resilience4j-core/src/main/java/io/github/resilience4j/core/EventProcessor.java index 315478da2d..d8104d27ef 100644 --- a/resilience4j-core/src/main/java/io/github/resilience4j/core/EventProcessor.java +++ b/resilience4j-core/src/main/java/io/github/resilience4j/core/EventProcessor.java @@ -20,37 +20,47 @@ import io.github.resilience4j.core.lang.Nullable; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CopyOnWriteArrayList; public class EventProcessor implements EventPublisher { private boolean consumerRegistered; - @Nullable private EventConsumer onEventConsumer; - private ConcurrentMap, EventConsumer> eventConsumers = new ConcurrentHashMap<>(); + List> onEventConsumers = new CopyOnWriteArrayList<>(); + ConcurrentMap>> eventConsumerMap = new ConcurrentHashMap<>(); public boolean hasConsumers(){ return consumerRegistered; } @SuppressWarnings("unchecked") - public synchronized void registerConsumer(Class eventType, EventConsumer eventConsumer){ - consumerRegistered = true; - eventConsumers.put(eventType, (EventConsumer) eventConsumer); + public synchronized void registerConsumer(String className, EventConsumer eventConsumer){ + this.consumerRegistered = true; + this.eventConsumerMap.compute(className, (k, consumers) -> { + if(consumers == null){ + consumers = new ArrayList<>(); + consumers.add((EventConsumer) eventConsumer); + return consumers; + }else{ + consumers.add((EventConsumer) eventConsumer); + return consumers; + } + }); } - @SuppressWarnings("unchecked") public boolean processEvent(E event) { boolean consumed = false; - EventConsumer onEventConsumer = this.onEventConsumer; - if(onEventConsumer != null){ - onEventConsumer.consumeEvent(event); + if(!onEventConsumers.isEmpty()){ + onEventConsumers.forEach(onEventConsumer -> onEventConsumer.consumeEvent(event)); consumed = true; } - if(!eventConsumers.isEmpty()){ - EventConsumer eventConsumer = (EventConsumer) eventConsumers.get(event.getClass()); - if(eventConsumer != null){ - eventConsumer.consumeEvent(event); + if(!eventConsumerMap.isEmpty()){ + List> eventConsumers = this.eventConsumerMap.get(event.getClass().getSimpleName()); + if(eventConsumers != null && !eventConsumers.isEmpty()){ + eventConsumers.forEach(consumer -> consumer.consumeEvent(event)); consumed = true; } } @@ -59,7 +69,7 @@ public boolean processEvent(E event) { @Override public synchronized void onEvent(@Nullable EventConsumer onEventConsumer) { - consumerRegistered = true; - this.onEventConsumer = onEventConsumer; + this.consumerRegistered = true; + this.onEventConsumers.add(onEventConsumer); } } diff --git a/resilience4j-core/src/main/java/io/github/resilience4j/core/Registry.java b/resilience4j-core/src/main/java/io/github/resilience4j/core/Registry.java index feacfd0535..3c5234541e 100644 --- a/resilience4j-core/src/main/java/io/github/resilience4j/core/Registry.java +++ b/resilience4j-core/src/main/java/io/github/resilience4j/core/Registry.java @@ -18,8 +18,12 @@ */ package io.github.resilience4j.core; +import io.github.resilience4j.core.registry.EntryAddedEvent; +import io.github.resilience4j.core.registry.EntryRemovedEvent; +import io.github.resilience4j.core.registry.EntryReplacedEvent; +import io.github.resilience4j.core.registry.RegistryEvent; + import java.util.Optional; -import java.util.function.Consumer; /** * root resilience4j registry to be used by resilience types registries for common functionality @@ -27,39 +31,59 @@ public interface Registry { /** + * Adds a configuration to the registry + * * @param configName the configuration name * @param configuration the added configuration */ void addConfiguration(String configName, Config configuration); + /** + * Remove an entry from the Registry + * + * @param name the name + */ + Optional remove(String name); + + /** + * Replace an existing entry in the Registry by a new one. + * + * @param name the existing name + * @param newEntry a new entry + */ + Optional replace(String name, Target newEntry); /** + * Get a configuration by name + * * @param configName the configuration name * @return the found configuration if any */ Optional getConfiguration(String configName); - /** - * Allows for configuring some functionality to be executed when a new target is created. + * Get the default configuration * - * @param postCreationConsumer A consumer function to execute for a target that was created. + * @return the default configuration */ - void registerPostCreationConsumer(Consumer postCreationConsumer); - + Config getDefaultConfig(); /** - * Allows for configuring some functionality to be executed when a new target is created. + * Returns an EventPublisher which can be used to register event consumers. * - * @param postCreationConsumer A consumer function to execute for a target that was created. + * @return an EventPublisher */ - void unregisterPostCreationConsumer(Consumer postCreationConsumer); - + EventPublisher getEventPublisher(); /** - * @return the default configuration of the target + * An EventPublisher can be used to register event consumers. */ - Config getDefaultConfig(); + interface EventPublisher extends io.github.resilience4j.core.EventPublisher { + + EventPublisher onEntryAdded(EventConsumer> eventConsumer); + EventPublisher onEntryRemoved(EventConsumer> eventConsumer); + EventPublisher onEntryReplaced(EventConsumer> eventConsumer); + } } diff --git a/resilience4j-core/src/main/java/io/github/resilience4j/core/AbstractRegistry.java b/resilience4j-core/src/main/java/io/github/resilience4j/core/registry/AbstractRegistry.java similarity index 51% rename from resilience4j-core/src/main/java/io/github/resilience4j/core/AbstractRegistry.java rename to resilience4j-core/src/main/java/io/github/resilience4j/core/registry/AbstractRegistry.java index 6f6d02b7f8..ab275f578d 100644 --- a/resilience4j-core/src/main/java/io/github/resilience4j/core/AbstractRegistry.java +++ b/resilience4j-core/src/main/java/io/github/resilience4j/core/registry/AbstractRegistry.java @@ -16,15 +16,16 @@ * * */ -package io.github.resilience4j.core; +package io.github.resilience4j.core.registry; + +import io.github.resilience4j.core.EventConsumer; +import io.github.resilience4j.core.EventProcessor; +import io.github.resilience4j.core.Registry; -import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.function.Consumer; import java.util.function.Supplier; /** @@ -36,30 +37,45 @@ public class AbstractRegistry implements Registry> postCreationConsumers; - /** * The map of targets by name */ - protected final ConcurrentMap targetMap; + protected final ConcurrentMap entryMap; /** * The map of shared configuration by name */ protected final ConcurrentMap configurations; + private final RegistryEventProcessor eventProcessor; + public AbstractRegistry(Config defaultConfig) { - this.postCreationConsumers = new CopyOnWriteArrayList<>(); this.configurations = new ConcurrentHashMap<>(); - this.targetMap = new ConcurrentHashMap<>(); + this.entryMap = new ConcurrentHashMap<>(); + this.eventProcessor = new RegistryEventProcessor(); this.configurations.put(DEFAULT_CONFIG, Objects.requireNonNull(defaultConfig, CONFIG_MUST_NOT_BE_NULL)); } protected Target computeIfAbsent(String name, Supplier supplier){ - return targetMap.computeIfAbsent(Objects.requireNonNull(name, NAME_MUST_NOT_BE_NULL), k -> notifyPostCreationConsumers(supplier.get())); + return entryMap.computeIfAbsent(Objects.requireNonNull(name, NAME_MUST_NOT_BE_NULL), k -> { + Target entry = supplier.get(); + eventProcessor.processEvent(new EntryAddedEvent<>(entry)); + return entry; + }); + } + + @Override + public Optional remove(String name){ + Optional removedEntry = Optional.ofNullable(entryMap.remove(name)); + removedEntry.ifPresent(entry -> eventProcessor.processEvent(new EntryRemovedEvent<>(entry))); + return removedEntry; + } + + @Override + public Optional replace(String name, Target newEntry){ + Optional replacedEntry = Optional.ofNullable(entryMap.replace(name, newEntry)); + replacedEntry.ifPresent(oldEntry -> eventProcessor.processEvent(new EntryReplacedEvent<>(oldEntry, newEntry))); + return replacedEntry; } @Override @@ -76,25 +92,39 @@ public Optional getConfiguration(String configName) { } @Override - public void registerPostCreationConsumer(Consumer postCreationConsumer) { - postCreationConsumers.add(postCreationConsumer); + public Config getDefaultConfig() { + return configurations.get(DEFAULT_CONFIG); } @Override - public void unregisterPostCreationConsumer(Consumer postCreationConsumer) { - postCreationConsumers.remove(postCreationConsumer); + public EventPublisher getEventPublisher() { + return eventProcessor; } - @Override - public Config getDefaultConfig() { - return configurations.get(DEFAULT_CONFIG); - } + private class RegistryEventProcessor extends EventProcessor implements EventConsumer, EventPublisher { + + @Override + public EventPublisher onEntryAdded(EventConsumer> onSuccessEventConsumer) { + registerConsumer(EntryAddedEvent.class.getSimpleName(), onSuccessEventConsumer); + return this; + } + + @Override + public EventPublisher onEntryRemoved(EventConsumer> onErrorEventConsumer) { + registerConsumer(EntryRemovedEvent.class.getSimpleName(), onErrorEventConsumer); + return this; + } + + @Override + public EventPublisher onEntryReplaced(EventConsumer> onStateTransitionEventConsumer) { + registerConsumer(EntryReplacedEvent.class.getSimpleName(), onStateTransitionEventConsumer); + return this; + } - protected Target notifyPostCreationConsumers(Target target) { - if (!postCreationConsumers.isEmpty()) { - postCreationConsumers.forEach(consumer -> consumer.accept(target)); + @Override + public void consumeEvent(RegistryEvent event) { + super.processEvent(event); } - return target; } } diff --git a/resilience4j-core/src/main/java/io/github/resilience4j/core/registry/AbstractRegistryEvent.java b/resilience4j-core/src/main/java/io/github/resilience4j/core/registry/AbstractRegistryEvent.java new file mode 100644 index 0000000000..a7af13feec --- /dev/null +++ b/resilience4j-core/src/main/java/io/github/resilience4j/core/registry/AbstractRegistryEvent.java @@ -0,0 +1,36 @@ +/* + * + * Copyright 2019: Robert Winkler + * + * 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.core.registry; + +import java.time.ZonedDateTime; + +abstract class AbstractRegistryEvent implements RegistryEvent { + + private final ZonedDateTime creationTime; + + + AbstractRegistryEvent() { + this.creationTime = ZonedDateTime.now(); + } + + @Override + public ZonedDateTime getCreationTime() { + return creationTime; + } +} \ No newline at end of file diff --git a/resilience4j-core/src/main/java/io/github/resilience4j/core/registry/EntryAddedEvent.java b/resilience4j-core/src/main/java/io/github/resilience4j/core/registry/EntryAddedEvent.java new file mode 100644 index 0000000000..2c8cb17f29 --- /dev/null +++ b/resilience4j-core/src/main/java/io/github/resilience4j/core/registry/EntryAddedEvent.java @@ -0,0 +1,38 @@ +/* + * + * Copyright 2019: Robert Winkler + * + * 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.core.registry; + + +public class EntryAddedEvent extends AbstractRegistryEvent { + + private Target addedEntry; + + EntryAddedEvent(Target addedEntry){ + this.addedEntry = addedEntry; + } + + @Override + public Type getEventType() { + return Type.ADDED; + } + + public Target getAddedEntry() { + return addedEntry; + } +} diff --git a/resilience4j-core/src/main/java/io/github/resilience4j/core/registry/EntryRemovedEvent.java b/resilience4j-core/src/main/java/io/github/resilience4j/core/registry/EntryRemovedEvent.java new file mode 100644 index 0000000000..aadaa8c7ae --- /dev/null +++ b/resilience4j-core/src/main/java/io/github/resilience4j/core/registry/EntryRemovedEvent.java @@ -0,0 +1,42 @@ +/* + * + * Copyright 2019: Robert Winkler + * + * 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.core.registry; + +public class EntryRemovedEvent extends AbstractRegistryEvent{ + + private Target removedEntry; + + EntryRemovedEvent(Target removedEntry){ + this.removedEntry = removedEntry; + } + + @Override + public Type getEventType() { + return Type.REMOVED; + } + + /** + * Returns the removed entry. + * + * @return the removed entry + */ + public Target getRemovedEntry() { + return removedEntry; + } +} diff --git a/resilience4j-core/src/main/java/io/github/resilience4j/core/registry/EntryReplacedEvent.java b/resilience4j-core/src/main/java/io/github/resilience4j/core/registry/EntryReplacedEvent.java new file mode 100644 index 0000000000..43a8f6e0d7 --- /dev/null +++ b/resilience4j-core/src/main/java/io/github/resilience4j/core/registry/EntryReplacedEvent.java @@ -0,0 +1,55 @@ +/* + * + * Copyright 2019: Robert Winkler + * + * 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.core.registry; + +public class EntryReplacedEvent extends AbstractRegistryEvent { + + private final Target oldEntry; + private final Target newEntry; + + EntryReplacedEvent(Target oldEntry, Target newEntry){ + super(); + this.oldEntry = oldEntry; + this.newEntry = newEntry; + } + + @Override + public Type getEventType() { + return Type.REPLACED; + } + + /** + * Returns the old entry. + * + * @return the old entry + */ + + public Target getOldEntry() { + return oldEntry; + } + + /** + * Returns the new entry. + * + * @return the new entry + */ + public Target getNewEntry() { + return newEntry; + } +} diff --git a/resilience4j-core/src/main/java/io/github/resilience4j/core/registry/RegistryEvent.java b/resilience4j-core/src/main/java/io/github/resilience4j/core/registry/RegistryEvent.java new file mode 100644 index 0000000000..42bd73f1ec --- /dev/null +++ b/resilience4j-core/src/main/java/io/github/resilience4j/core/registry/RegistryEvent.java @@ -0,0 +1,50 @@ +/* + * + * Copyright 2019: Robert Winkler + * + * 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.core.registry; + +import java.time.ZonedDateTime; + +public interface RegistryEvent { + + /** + * Returns the type of the Registry event. + * + * @return the type of the Registry event + */ + Type getEventType(); + + /** + * Returns the creation time of Registry event. + * + * @return the creation time of Registry event + */ + ZonedDateTime getCreationTime(); + + /** + * Event types which are created by a CircuitBreaker. + */ + enum Type { + /** An Event which informs that an entry has been added */ + ADDED, + /** An Event which informs that an entry has been removed */ + REMOVED, + /** An Event which informs that an entry has been replaced */ + REPLACED + } +} diff --git a/resilience4j-core/src/main/java/io/github/resilience4j/core/Event.java b/resilience4j-core/src/main/java/io/github/resilience4j/core/registry/package-info.java similarity index 73% rename from resilience4j-core/src/main/java/io/github/resilience4j/core/Event.java rename to resilience4j-core/src/main/java/io/github/resilience4j/core/registry/package-info.java index 7ff3a703b0..42f90015d2 100644 --- a/resilience4j-core/src/main/java/io/github/resilience4j/core/Event.java +++ b/resilience4j-core/src/main/java/io/github/resilience4j/core/registry/package-info.java @@ -1,6 +1,6 @@ /* * - * Copyright 2017: Robert Winkler + * Copyright 2018: Clint Checketts * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,9 @@ * * */ -package io.github.resilience4j.core; +@NonNullApi +@NonNullFields +package io.github.resilience4j.core.registry; -public interface 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-core/src/test/java/io/github/resilience4j/core/AbstractRegistryTest.java b/resilience4j-core/src/test/java/io/github/resilience4j/core/AbstractRegistryTest.java deleted file mode 100644 index 974724ef3c..0000000000 --- a/resilience4j-core/src/test/java/io/github/resilience4j/core/AbstractRegistryTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.github.resilience4j.core; - -import org.junit.Before; -import org.junit.Test; -import org.slf4j.Logger; - -import java.util.function.Consumer; - -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 AbstractRegistryTest { - - private Logger LOGGER; - - @Before - public void setUp() { - LOGGER = mock(Logger.class); - } - - @Test - public void testAbstractRegistryActions() { - Consumer consumer1 = circuitBreaker -> LOGGER.info("invoking the post consumer1"); - TestRegistry testRegistry = new TestRegistry(); - testRegistry.registerPostCreationConsumer(consumer1); - testRegistry.addConfiguration("test", "test"); - assertThat(testRegistry.getConfiguration("test").get()).isEqualTo("test"); - assertThat(testRegistry.getDefaultConfig()).isEqualTo("default"); - testRegistry.notifyPostCreationConsumers("test"); - then(LOGGER).should(times(1)).info("invoking the post consumer1"); - testRegistry.unregisterPostCreationConsumer(consumer1); - testRegistry.notifyPostCreationConsumers("test"); - then(LOGGER).shouldHaveZeroInteractions(); - } - - class TestRegistry extends AbstractRegistry { - - public TestRegistry() { - super( "default"); - this.configurations.put(DEFAULT_CONFIG, "default"); - - } - } - -} \ No newline at end of file diff --git a/resilience4j-core/src/test/java/io/github/resilience4j/core/EventProcessorTest.java b/resilience4j-core/src/test/java/io/github/resilience4j/core/EventProcessorTest.java index 0a04a42f3e..10c39194f3 100644 --- a/resilience4j-core/src/test/java/io/github/resilience4j/core/EventProcessorTest.java +++ b/resilience4j-core/src/test/java/io/github/resilience4j/core/EventProcessorTest.java @@ -37,31 +37,63 @@ public void setUp(){ } @Test - public void testOnEventConsumer(){ + public void testRegisterOnEventConsumer(){ EventProcessor eventProcessor = new EventProcessor<>(); - eventProcessor.onEvent(event -> logger.info(event.toString())); + EventConsumer eventConsumer = event -> logger.info(event.toString()); + eventProcessor.onEvent(eventConsumer); + eventProcessor.onEvent(eventConsumer); + + assertThat(eventProcessor.onEventConsumers).hasSize(2); boolean consumed = eventProcessor.processEvent(1); - then(logger).should(times(1)).info("1"); + then(logger).should(times(2)).info("1"); assertThat(consumed).isEqualTo(true); } @Test - public void testRegisterConsumer() throws InterruptedException { + public void testRegisterConsumer() { EventProcessor eventProcessor = new EventProcessor<>(); - eventProcessor.registerConsumer(Integer.class, event -> logger.info(event.toString())); + EventConsumer eventConsumer = event -> logger.info(event.toString()); + eventProcessor.registerConsumer(Integer.class.getSimpleName(), eventConsumer); + eventProcessor.registerConsumer(Integer.class.getSimpleName(), eventConsumer); + + assertThat(eventProcessor.eventConsumerMap).hasSize(1); + assertThat(eventProcessor.eventConsumerMap.get(Integer.class.getSimpleName())).hasSize(2); boolean consumed = eventProcessor.processEvent(1); - then(logger).should(times(1)).info("1"); + then(logger).should(times(2)).info("1"); assertThat(consumed).isEqualTo(true); } @Test - public void testOnEventAndRegisterConsumer() throws InterruptedException { + public void testRegisterDifferentConsumers() { + EventProcessor eventProcessor = new EventProcessor<>(); + EventConsumer integerConsumer = event -> logger.info(event.toString()); + EventConsumer floatConsumer = event -> logger.info(event.toString()); + eventProcessor.registerConsumer(Integer.class.getSimpleName(), integerConsumer); + eventProcessor.registerConsumer(Float.class.getSimpleName(), floatConsumer); + + assertThat(eventProcessor.eventConsumerMap).hasSize(2); + assertThat(eventProcessor.eventConsumerMap.get(Integer.class.getSimpleName())).hasSize(1); + assertThat(eventProcessor.eventConsumerMap.get(Float.class.getSimpleName())).hasSize(1); + + boolean consumed = eventProcessor.processEvent(1); + assertThat(consumed).isEqualTo(true); + + consumed = eventProcessor.processEvent(1.0f); + assertThat(consumed).isEqualTo(true); + + then(logger).should(times(1)).info("1"); + then(logger).should(times(1)).info("1.0"); + } + + @Test + public void testOnEventAndRegisterConsumer() { EventProcessor eventProcessor = new EventProcessor<>(); - eventProcessor.registerConsumer(Integer.class, event -> logger.info(event.toString())); + EventConsumer eventConsumer = event -> logger.info(event.toString()); + eventProcessor.registerConsumer(Integer.class.getSimpleName(), eventConsumer); eventProcessor.onEvent(event -> logger.info(event.toString())); boolean consumed = eventProcessor.processEvent(1); @@ -71,7 +103,7 @@ public void testOnEventAndRegisterConsumer() throws InterruptedException { } @Test - public void testNoConsumers() throws InterruptedException { + public void testNoConsumers() { EventProcessor eventProcessor = new EventProcessor<>(); boolean consumed = eventProcessor.processEvent(1); diff --git a/resilience4j-core/src/test/java/io/github/resilience4j/core/registry/AbstractRegistryTest.java b/resilience4j-core/src/test/java/io/github/resilience4j/core/registry/AbstractRegistryTest.java new file mode 100644 index 0000000000..d174d74dae --- /dev/null +++ b/resilience4j-core/src/test/java/io/github/resilience4j/core/registry/AbstractRegistryTest.java @@ -0,0 +1,79 @@ +package io.github.resilience4j.core.registry; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static io.github.resilience4j.core.registry.RegistryEvent.Type; +import static org.assertj.core.api.Assertions.assertThat; + + +public class AbstractRegistryTest { + + @Test + public void shouldContainDefaultAndCustomConfiguration() { + TestRegistry testRegistry = new TestRegistry(); + testRegistry.addConfiguration("custom", "test"); + assertThat(testRegistry.getConfiguration("custom").get()).isEqualTo("test"); + assertThat(testRegistry.getDefaultConfig()).isEqualTo("default"); + } + + @Test + public void shouldConsumeRegistryEvents() { + List consumedEvents = new ArrayList<>(); + List> addedEvents = new ArrayList<>(); + List> removedEvents = new ArrayList<>(); + List> replacedEvents = new ArrayList<>(); + + TestRegistry testRegistry = new TestRegistry(); + testRegistry.getEventPublisher().onEvent(consumedEvents::add); + testRegistry.getEventPublisher().onEntryAdded(addedEvents::add); + testRegistry.getEventPublisher().onEntryRemoved(removedEvents::add); + testRegistry.getEventPublisher().onEntryReplaced(replacedEvents::add); + + String addedEntry1 = testRegistry.computeIfAbsent("name", () -> "entry1"); + assertThat(addedEntry1).isEqualTo("entry1"); + + String addedEntry2 = testRegistry.computeIfAbsent("name2", () -> "entry2"); + assertThat(addedEntry2).isEqualTo("entry2"); + + Optional removedEntry = testRegistry.remove("name"); + assertThat(removedEntry).isNotEmpty().hasValue("entry1"); + + Optional replacedEntry = testRegistry.replace("name2", "entry3"); + assertThat(replacedEntry).isNotEmpty().hasValue("entry2"); + + assertThat(consumedEvents).hasSize(4); + assertThat(addedEvents).hasSize(2); + assertThat(removedEvents).hasSize(1); + assertThat(replacedEvents).hasSize(1); + + assertThat(consumedEvents).extracting("eventType") + .containsExactly(Type.ADDED, Type.ADDED, Type.REMOVED, Type.REPLACED); + + assertThat(addedEvents).extracting("addedEntry") + .containsExactly("entry1", "entry2"); + + assertThat(removedEvents).extracting("removedEntry") + .containsExactly("entry1"); + + assertThat(replacedEvents).extracting("oldEntry") + .containsExactly("entry2"); + + assertThat(replacedEvents).extracting("newEntry") + .containsExactly("entry3"); + + } + + class TestRegistry extends AbstractRegistry { + + public TestRegistry() { + super( "default"); + this.configurations.put(DEFAULT_CONFIG, "default"); + + } + } + +} \ No newline at end of file diff --git a/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/BulkheadMetrics.java b/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/BulkheadMetrics.java index b5068376f8..8b5380b009 100644 --- a/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/BulkheadMetrics.java +++ b/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/BulkheadMetrics.java @@ -17,6 +17,7 @@ import io.github.resilience4j.bulkhead.Bulkhead; import io.github.resilience4j.bulkhead.BulkheadRegistry; +import io.github.resilience4j.micrometer.tagged.TaggedBulkheadMetrics; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.binder.MeterBinder; @@ -27,6 +28,10 @@ import static io.github.resilience4j.micrometer.MetricUtils.getName; import static java.util.Objects.requireNonNull; +/** + * @deprecated Use {@link TaggedBulkheadMetrics} instead + */ +@Deprecated public class BulkheadMetrics implements MeterBinder { private final Iterable bulkheads; diff --git a/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/CircuitBreakerMetrics.java b/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/CircuitBreakerMetrics.java index 6473f2d69e..c9553ec685 100644 --- a/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/CircuitBreakerMetrics.java +++ b/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/CircuitBreakerMetrics.java @@ -17,6 +17,7 @@ import io.github.resilience4j.circuitbreaker.CircuitBreaker; import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; +import io.github.resilience4j.micrometer.tagged.TaggedCircuitBreakerMetrics; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.binder.MeterBinder; @@ -25,6 +26,10 @@ import static io.github.resilience4j.micrometer.MetricUtils.getName; import static java.util.Objects.requireNonNull; +/** + * @deprecated Use {@link TaggedCircuitBreakerMetrics} instead + */ +@Deprecated public class CircuitBreakerMetrics implements MeterBinder { private final Iterable circuitBreakers; diff --git a/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/RateLimiterMetrics.java b/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/RateLimiterMetrics.java index 59986b1096..990a21fff6 100644 --- a/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/RateLimiterMetrics.java +++ b/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/RateLimiterMetrics.java @@ -15,6 +15,7 @@ */ package io.github.resilience4j.micrometer; +import io.github.resilience4j.micrometer.tagged.TaggedRateLimiterMetrics; import io.github.resilience4j.ratelimiter.RateLimiter; import io.github.resilience4j.ratelimiter.RateLimiterRegistry; import io.micrometer.core.instrument.Gauge; @@ -27,6 +28,10 @@ import static io.github.resilience4j.ratelimiter.utils.MetricNames.WAITING_THREADS; import static java.util.Objects.requireNonNull; +/** + * @deprecated Use {@link TaggedRateLimiterMetrics} instead + */ +@Deprecated public class RateLimiterMetrics implements MeterBinder { private final Iterable rateLimiters; diff --git a/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/RetryMetrics.java b/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/RetryMetrics.java index 0c72cb81d6..3c883feac0 100644 --- a/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/RetryMetrics.java +++ b/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/RetryMetrics.java @@ -15,14 +15,7 @@ */ package io.github.resilience4j.micrometer; -import static io.github.resilience4j.micrometer.MetricUtils.getName; -import static io.github.resilience4j.retry.utils.MetricNames.DEFAULT_PREFIX; -import static io.github.resilience4j.retry.utils.MetricNames.FAILED_CALLS_WITHOUT_RETRY; -import static io.github.resilience4j.retry.utils.MetricNames.FAILED_CALLS_WITH_RETRY; -import static io.github.resilience4j.retry.utils.MetricNames.SUCCESSFUL_CALLS_WITHOUT_RETRY; -import static io.github.resilience4j.retry.utils.MetricNames.SUCCESSFUL_CALLS_WITH_RETRY; -import static java.util.Objects.requireNonNull; - +import io.github.resilience4j.micrometer.tagged.TaggedRetryMetrics; import io.github.resilience4j.ratelimiter.RateLimiterRegistry; import io.github.resilience4j.retry.Retry; import io.github.resilience4j.retry.RetryRegistry; @@ -30,6 +23,14 @@ import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.binder.MeterBinder; +import static io.github.resilience4j.micrometer.MetricUtils.getName; +import static io.github.resilience4j.retry.utils.MetricNames.*; +import static java.util.Objects.requireNonNull; + +/** + * @deprecated Use {@link TaggedRetryMetrics} instead + */ +@Deprecated public class RetryMetrics implements MeterBinder { private final Iterable retries; diff --git a/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/AbstractMetrics.java b/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/AbstractMetrics.java new file mode 100644 index 0000000000..1980b90afe --- /dev/null +++ b/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/AbstractMetrics.java @@ -0,0 +1,25 @@ +package io.github.resilience4j.micrometer.tagged; + +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.MeterRegistry; + +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +abstract class AbstractMetrics { + + protected ConcurrentMap> meterIdMap; + + AbstractMetrics() { + this.meterIdMap = new ConcurrentHashMap<>(); + } + + void removeMetrics(MeterRegistry registry, String name) { + Set ids = meterIdMap.get(name); + if(ids != null){ + ids.forEach(registry::remove); + } + meterIdMap.remove(name); + } +} diff --git a/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/TaggedBulkheadMetrics.java b/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/TaggedBulkheadMetrics.java index b240d56022..e3be1ddd0b 100644 --- a/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/TaggedBulkheadMetrics.java +++ b/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/TaggedBulkheadMetrics.java @@ -20,9 +20,13 @@ import io.github.resilience4j.bulkhead.BulkheadRegistry; import io.github.resilience4j.micrometer.BulkheadMetrics; import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.binder.MeterBinder; +import java.util.HashSet; +import java.util.Set; + import static java.util.Objects.requireNonNull; /** @@ -30,16 +34,16 @@ * The main difference from {@link BulkheadMetrics} is that this binder uses tags * to distinguish between metrics. */ -public class TaggedBulkheadMetrics implements MeterBinder { +public class TaggedBulkheadMetrics extends AbstractMetrics implements MeterBinder { /** * Creates a new binder that uses given {@code registry} as source of bulkheads. * - * @param registry the source of bulkheads + * @param bulkheadRegistry the source of bulkheads * @return The {@link TaggedBulkheadMetrics} instance. */ - public static TaggedBulkheadMetrics ofBulkheadRegistry(BulkheadRegistry registry) { - return new TaggedBulkheadMetrics(MetricNames.ofDefaults(), registry.getAllBulkheads()); + public static TaggedBulkheadMetrics ofBulkheadRegistry(BulkheadRegistry bulkheadRegistry) { + return new TaggedBulkheadMetrics(MetricNames.ofDefaults(), bulkheadRegistry); } /** @@ -47,31 +51,46 @@ public static TaggedBulkheadMetrics ofBulkheadRegistry(BulkheadRegistry registry * using given {@code registry} as source of bulkheads. * * @param names custom names of the metrics - * @param registry the source of bulkheads + * @param bulkheadRegistry the source of bulkheads * @return The {@link TaggedBulkheadMetrics} instance. */ - public static TaggedBulkheadMetrics ofBulkheadRegistry(MetricNames names, BulkheadRegistry registry) { - return new TaggedBulkheadMetrics(names, registry.getAllBulkheads()); + public static TaggedBulkheadMetrics ofBulkheadRegistry(MetricNames names, BulkheadRegistry bulkheadRegistry) { + return new TaggedBulkheadMetrics(names, bulkheadRegistry); } private final MetricNames names; - private final Iterable bulkheads; + private final BulkheadRegistry bulkheadRegistry; - private TaggedBulkheadMetrics(MetricNames names, Iterable bulkheads) { + private TaggedBulkheadMetrics(MetricNames names, BulkheadRegistry bulkheadRegistry) { + super(); this.names = requireNonNull(names); - this.bulkheads = requireNonNull(bulkheads); + this.bulkheadRegistry = requireNonNull(bulkheadRegistry); } @Override public void bindTo(MeterRegistry registry) { - for (Bulkhead bulkhead : bulkheads) { - Gauge.builder(names.getAvailableConcurrentCallsMetricName(), bulkhead, (bh) -> bh.getMetrics().getAvailableConcurrentCalls()) - .tag(TagNames.NAME, bulkhead.getName()) - .register(registry); - Gauge.builder(names.getMaxAllowedConcurrentCallsMetricName(), bulkhead, (bh) -> bh.getMetrics().getMaxAllowedConcurrentCalls()) - .tag(TagNames.NAME, bulkhead.getName()) - .register(registry); + for (Bulkhead bulkhead : bulkheadRegistry.getAllBulkheads()) { + addMetrics(registry, bulkhead); } + bulkheadRegistry.getEventPublisher().onEntryAdded(event -> addMetrics(registry, event.getAddedEntry())); + bulkheadRegistry.getEventPublisher().onEntryRemoved(event -> removeMetrics(registry, event.getRemovedEntry().getName())); + bulkheadRegistry.getEventPublisher().onEntryReplaced(event -> { + removeMetrics(registry, event.getOldEntry().getName()); + addMetrics(registry, event.getNewEntry()); + }); + } + + private void addMetrics(MeterRegistry registry, Bulkhead bulkhead) { + Set idSet = new HashSet<>(); + + idSet.add(Gauge.builder(names.getAvailableConcurrentCallsMetricName(), bulkhead, (bh) -> bh.getMetrics().getAvailableConcurrentCalls()) + .tag(TagNames.NAME, bulkhead.getName()) + .register(registry).getId()); + idSet.add(Gauge.builder(names.getMaxAllowedConcurrentCallsMetricName(), bulkhead, (bh) -> bh.getMetrics().getMaxAllowedConcurrentCalls()) + .tag(TagNames.NAME, bulkhead.getName()) + .register(registry).getId()); + + meterIdMap.put(bulkhead.getName(), idSet); } /** Defines possible configuration for metric names. */ diff --git a/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/TaggedCircuitBreakerMetrics.java b/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/TaggedCircuitBreakerMetrics.java index 649d3013e8..659b047d88 100644 --- a/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/TaggedCircuitBreakerMetrics.java +++ b/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/TaggedCircuitBreakerMetrics.java @@ -20,9 +20,13 @@ import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; import io.github.resilience4j.micrometer.CircuitBreakerMetrics; import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.binder.MeterBinder; +import java.util.HashSet; +import java.util.Set; + import static java.util.Objects.requireNonNull; /** @@ -30,65 +34,77 @@ * The main difference from {@link CircuitBreakerMetrics} is that this binder uses tags * to distinguish between circuit breaker instances. */ -public class TaggedCircuitBreakerMetrics implements MeterBinder { +public class TaggedCircuitBreakerMetrics extends AbstractMetrics implements MeterBinder { /** * Creates a new binder that uses given {@code registry} as source of circuit breakers. * - * @param metricNames custom metric names - * @param registry the source of circuit breakers + * @param circuitBreakerRegistry the source of circuit breakers * @return The {@link TaggedCircuitBreakerMetrics} instance. */ - public static TaggedCircuitBreakerMetrics ofCircuitBreakerRegistry(MetricNames metricNames, CircuitBreakerRegistry registry) { - return new TaggedCircuitBreakerMetrics(metricNames, registry.getAllCircuitBreakers()); + public static TaggedCircuitBreakerMetrics ofCircuitBreakerRegistry(CircuitBreakerRegistry circuitBreakerRegistry) { + return new TaggedCircuitBreakerMetrics(MetricNames.ofDefaults(), circuitBreakerRegistry); } /** * Creates a new binder that uses given {@code registry} as source of circuit breakers. * - * @param registry the source of circuit breakers + * @param metricNames custom metric names + * @param circuitBreakerRegistry the source of circuit breakers * @return The {@link TaggedCircuitBreakerMetrics} instance. */ - public static TaggedCircuitBreakerMetrics ofCircuitBreakerRegistry(CircuitBreakerRegistry registry) { - return ofCircuitBreakerRegistry(MetricNames.ofDefaults(), registry); + public static TaggedCircuitBreakerMetrics ofCircuitBreakerRegistry(MetricNames metricNames, CircuitBreakerRegistry circuitBreakerRegistry) { + return new TaggedCircuitBreakerMetrics(metricNames, circuitBreakerRegistry); } private final MetricNames names; - private final Iterable circuitBreakers; + private final CircuitBreakerRegistry circuitBreakerRegistry; - private TaggedCircuitBreakerMetrics(MetricNames names, Iterable circuitBreakers) { + private TaggedCircuitBreakerMetrics(MetricNames names, CircuitBreakerRegistry circuitBreakerRegistry) { + super(); this.names = requireNonNull(names); - this.circuitBreakers = requireNonNull(circuitBreakers); + this.circuitBreakerRegistry = requireNonNull(circuitBreakerRegistry); + } + + private void addMetrics(MeterRegistry registry, CircuitBreaker circuitBreaker) { + Set idSet = new HashSet<>(); + + idSet.add(Gauge.builder(names.getStateMetricName(), circuitBreaker, (cb) -> cb.getState().getOrder()) + .tag(TagNames.NAME, circuitBreaker.getName()) + .register(registry).getId()); + idSet.add(Gauge.builder(names.getCallsMetricName(), circuitBreaker, (cb) -> cb.getMetrics().getNumberOfFailedCalls()) + .tag(TagNames.NAME, circuitBreaker.getName()) + .tag(TagNames.KIND, "failed") + .register(registry).getId()); + idSet.add(Gauge.builder(names.getCallsMetricName(), circuitBreaker, (cb) -> cb.getMetrics().getNumberOfNotPermittedCalls()) + .tag(TagNames.NAME, circuitBreaker.getName()) + .tag(TagNames.KIND, "not_permitted") + .register(registry).getId()); + idSet.add(Gauge.builder(names.getCallsMetricName(), circuitBreaker, (cb) -> cb.getMetrics().getNumberOfSuccessfulCalls()) + .tag(TagNames.NAME, circuitBreaker.getName()) + .tag(TagNames.KIND, "successful") + .register(registry).getId()); + idSet.add(Gauge.builder(names.getBufferedCallsMetricName(), circuitBreaker, (cb) -> cb.getMetrics().getNumberOfBufferedCalls()) + .tag(TagNames.NAME, circuitBreaker.getName()) + .register(registry).getId()); + idSet.add(Gauge.builder(names.getMaxBufferedCallsMetricName(), circuitBreaker, (cb) -> cb.getMetrics().getMaxNumberOfBufferedCalls()) + .tag(TagNames.NAME, circuitBreaker.getName()) + .register(registry).getId()); + + meterIdMap.put(circuitBreaker.getName(), idSet); } @Override public void bindTo(MeterRegistry registry) { - for (CircuitBreaker circuitBreaker : circuitBreakers) { - Gauge.builder(names.getStateMetricName(), circuitBreaker, (cb) -> cb.getState().getOrder()) - .tag(TagNames.NAME, circuitBreaker.getName()) - .register(registry); - - Gauge.builder(names.getCallsMetricName(), circuitBreaker, (cb) -> cb.getMetrics().getNumberOfFailedCalls()) - .tag(TagNames.NAME, circuitBreaker.getName()) - .tag(TagNames.KIND, "failed") - .register(registry); - Gauge.builder(names.getCallsMetricName(), circuitBreaker, (cb) -> cb.getMetrics().getNumberOfNotPermittedCalls()) - .tag(TagNames.NAME, circuitBreaker.getName()) - .tag(TagNames.KIND, "not_permitted") - .register(registry); - Gauge.builder(names.getCallsMetricName(), circuitBreaker, (cb) -> cb.getMetrics().getNumberOfSuccessfulCalls()) - .tag(TagNames.NAME, circuitBreaker.getName()) - .tag(TagNames.KIND, "successful") - .register(registry); - - Gauge.builder(names.getBufferedCallsMetricName(), circuitBreaker, (cb) -> cb.getMetrics().getNumberOfBufferedCalls()) - .tag(TagNames.NAME, circuitBreaker.getName()) - .register(registry); - - Gauge.builder(names.getMaxBufferedCallsMetricName(), circuitBreaker, (cb) -> cb.getMetrics().getMaxNumberOfBufferedCalls()) - .tag(TagNames.NAME, circuitBreaker.getName()) - .register(registry); + for (CircuitBreaker circuitBreaker : circuitBreakerRegistry.getAllCircuitBreakers()) { + addMetrics(registry, circuitBreaker); } + circuitBreakerRegistry.getEventPublisher().onEntryAdded(event -> addMetrics(registry, event.getAddedEntry())); + circuitBreakerRegistry.getEventPublisher().onEntryRemoved(event -> removeMetrics(registry, event.getRemovedEntry().getName())); + circuitBreakerRegistry.getEventPublisher().onEntryReplaced(event -> { + removeMetrics(registry, event.getOldEntry().getName()); + addMetrics(registry, event.getNewEntry()); + }); } /** Defines possible configuration for metric names. */ diff --git a/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/TaggedRateLimiterMetrics.java b/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/TaggedRateLimiterMetrics.java index 05c8fdf166..499000e110 100644 --- a/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/TaggedRateLimiterMetrics.java +++ b/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/TaggedRateLimiterMetrics.java @@ -20,10 +20,13 @@ import io.github.resilience4j.ratelimiter.RateLimiter.Metrics; import io.github.resilience4j.ratelimiter.RateLimiterRegistry; import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.binder.MeterBinder; +import java.util.HashSet; import java.util.Objects; +import java.util.Set; import static java.util.Objects.requireNonNull; @@ -32,47 +35,62 @@ * The main difference from {@link RateLimiterMetrics} is that this binder uses tags * to distinguish between metrics. */ -public class TaggedRateLimiterMetrics implements MeterBinder { +public class TaggedRateLimiterMetrics extends AbstractMetrics implements MeterBinder { /** * Creates a new binder that uses given {@code registry} as source of retries. * - * @param registry the source of retries + * @param rateLimiterRegistry the source of retries * @return The {@link TaggedRateLimiterMetrics} instance. */ - public static TaggedRateLimiterMetrics ofRateLimiterRegistry(RateLimiterRegistry registry) { - return new TaggedRateLimiterMetrics(MetricNames.ofDefaults(), registry.getAllRateLimiters()); + public static TaggedRateLimiterMetrics ofRateLimiterRegistry(RateLimiterRegistry rateLimiterRegistry) { + return new TaggedRateLimiterMetrics(MetricNames.ofDefaults(), rateLimiterRegistry); } /** * Creates a new binder that uses given {@code registry} as source of retries. * * @param names custom metric names - * @param registry the source of rate limiters + * @param rateLimiterRegistry the source of rate limiters * @return The {@link TaggedRateLimiterMetrics} instance. */ - public static TaggedRateLimiterMetrics ofRateLimiterRegistry(MetricNames names, RateLimiterRegistry registry) { - return new TaggedRateLimiterMetrics(names, registry.getAllRateLimiters()); + public static TaggedRateLimiterMetrics ofRateLimiterRegistry(MetricNames names, RateLimiterRegistry rateLimiterRegistry) { + return new TaggedRateLimiterMetrics(names, rateLimiterRegistry); } private final MetricNames names; - private final Iterable rateLimiters; + private final RateLimiterRegistry rateLimiterRegistry; - private TaggedRateLimiterMetrics(MetricNames names, Iterable rateLimiters) { + private TaggedRateLimiterMetrics(MetricNames names, RateLimiterRegistry rateLimiterRegistry) { + super(); this.names = Objects.requireNonNull(names); - this.rateLimiters = Objects.requireNonNull(rateLimiters); + this.rateLimiterRegistry = Objects.requireNonNull(rateLimiterRegistry); } @Override public void bindTo(MeterRegistry registry) { - for (RateLimiter rateLimiter : rateLimiters) { - Gauge.builder(names.getAvailablePermissionsMetricName(), rateLimiter, (rl) -> rl.getMetrics().getAvailablePermissions()) - .tag(TagNames.NAME, rateLimiter.getName()) - .register(registry); - Gauge.builder(names.getWaitingThreadsMetricName(), rateLimiter, (rl) -> rl.getMetrics().getNumberOfWaitingThreads()) - .tag(TagNames.NAME, rateLimiter.getName()) - .register(registry); + for (RateLimiter rateLimiter : rateLimiterRegistry.getAllRateLimiters()) { + addMetrics(registry, rateLimiter); } + rateLimiterRegistry.getEventPublisher().onEntryAdded(event -> addMetrics(registry, event.getAddedEntry())); + rateLimiterRegistry.getEventPublisher().onEntryRemoved(event -> removeMetrics(registry, event.getRemovedEntry().getName())); + rateLimiterRegistry.getEventPublisher().onEntryReplaced(event -> { + removeMetrics(registry, event.getOldEntry().getName()); + addMetrics(registry, event.getNewEntry()); + }); + } + + private void addMetrics(MeterRegistry registry, RateLimiter rateLimiter) { + Set idSet = new HashSet<>(); + + idSet.add(Gauge.builder(names.getAvailablePermissionsMetricName(), rateLimiter, (rl) -> rl.getMetrics().getAvailablePermissions()) + .tag(TagNames.NAME, rateLimiter.getName()) + .register(registry).getId()); + idSet.add(Gauge.builder(names.getWaitingThreadsMetricName(), rateLimiter, (rl) -> rl.getMetrics().getNumberOfWaitingThreads()) + .tag(TagNames.NAME, rateLimiter.getName()) + .register(registry).getId()); + + meterIdMap.put(rateLimiter.getName(), idSet); } /** Defines possible configuration for metric names. */ diff --git a/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/TaggedRetryMetrics.java b/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/TaggedRetryMetrics.java index 3f7dbb7cec..36103fe054 100644 --- a/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/TaggedRetryMetrics.java +++ b/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/TaggedRetryMetrics.java @@ -19,9 +19,13 @@ import io.github.resilience4j.retry.Retry; import io.github.resilience4j.retry.RetryRegistry; import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.binder.MeterBinder; +import java.util.HashSet; +import java.util.Set; + import static io.github.resilience4j.retry.Retry.Metrics; import static java.util.Objects.requireNonNull; @@ -30,57 +34,72 @@ * The main difference from {@link RetryMetrics} is that this binder uses tags * to distinguish between metrics. */ -public class TaggedRetryMetrics implements MeterBinder { +public class TaggedRetryMetrics extends AbstractMetrics implements MeterBinder { /** * Creates a new binder that uses given {@code registry} as source of retries. * - * @param registry the source of retries + * @param retryRegistry the source of retries * @return The {@link TaggedRetryMetrics} instance. */ - public static TaggedRetryMetrics ofRetryRegistry(RetryRegistry registry) { - return new TaggedRetryMetrics(MetricNames.ofDefaults(), registry.getAllRetries()); + public static TaggedRetryMetrics ofRetryRegistry(RetryRegistry retryRegistry) { + return new TaggedRetryMetrics(MetricNames.ofDefaults(), retryRegistry); } /** * Creates a new binder that uses given {@code registry} as source of retries. * * @param names custom metric names - * @param registry the source of retries + * @param retryRegistry the source of retries * @return The {@link TaggedRetryMetrics} instance. */ - public static TaggedRetryMetrics ofRetryRegistry(MetricNames names, RetryRegistry registry) { - return new TaggedRetryMetrics(names, registry.getAllRetries()); + public static TaggedRetryMetrics ofRetryRegistry(MetricNames names, RetryRegistry retryRegistry) { + return new TaggedRetryMetrics(names, retryRegistry); } private final MetricNames names; - private final Iterable retries; + private final RetryRegistry retryRegistry; - private TaggedRetryMetrics(MetricNames names, Iterable retries) { + private TaggedRetryMetrics(MetricNames names, RetryRegistry retryRegistry) { + super(); this.names = requireNonNull(names); - this.retries = requireNonNull(retries); + this.retryRegistry = requireNonNull(retryRegistry); } @Override public void bindTo(MeterRegistry registry) { - for (Retry retry : retries) { - Gauge.builder(names.getCallsMetricName(), retry, (rt) -> rt.getMetrics().getNumberOfSuccessfulCallsWithoutRetryAttempt()) - .tag(TagNames.NAME, retry.getName()) - .tag(TagNames.KIND, "successful_without_retry") - .register(registry); - Gauge.builder(names.getCallsMetricName(), retry, (rt) -> rt.getMetrics().getNumberOfSuccessfulCallsWithRetryAttempt()) - .tag(TagNames.NAME, retry.getName()) - .tag(TagNames.KIND, "successful_with_retry") - .register(registry); - Gauge.builder(names.getCallsMetricName(), retry, (rt) -> rt.getMetrics().getNumberOfFailedCallsWithoutRetryAttempt()) - .tag(TagNames.NAME, retry.getName()) - .tag(TagNames.KIND, "failed_without_retry") - .register(registry); - Gauge.builder(names.getCallsMetricName(), retry, (rt) -> rt.getMetrics().getNumberOfFailedCallsWithRetryAttempt()) - .tag(TagNames.NAME, retry.getName()) - .tag(TagNames.KIND, "failed_with_retry") - .register(registry); + for (Retry retry : retryRegistry.getAllRetries()) { + addMetrics(registry, retry); } + retryRegistry.getEventPublisher().onEntryAdded(event -> addMetrics(registry, event.getAddedEntry())); + retryRegistry.getEventPublisher().onEntryRemoved(event -> removeMetrics(registry, event.getRemovedEntry().getName())); + retryRegistry.getEventPublisher().onEntryReplaced(event -> { + removeMetrics(registry, event.getOldEntry().getName()); + addMetrics(registry, event.getNewEntry()); + }); + } + + private void addMetrics(MeterRegistry registry, Retry retry) { + Set idSet = new HashSet<>(); + + idSet.add(Gauge.builder(names.getCallsMetricName(), retry, (rt) -> rt.getMetrics().getNumberOfSuccessfulCallsWithoutRetryAttempt()) + .tag(TagNames.NAME, retry.getName()) + .tag(TagNames.KIND, "successful_without_retry") + .register(registry).getId()); + idSet.add(Gauge.builder(names.getCallsMetricName(), retry, (rt) -> rt.getMetrics().getNumberOfSuccessfulCallsWithRetryAttempt()) + .tag(TagNames.NAME, retry.getName()) + .tag(TagNames.KIND, "successful_with_retry") + .register(registry).getId()); + idSet.add(Gauge.builder(names.getCallsMetricName(), retry, (rt) -> rt.getMetrics().getNumberOfFailedCallsWithoutRetryAttempt()) + .tag(TagNames.NAME, retry.getName()) + .tag(TagNames.KIND, "failed_without_retry") + .register(registry).getId()); + idSet.add(Gauge.builder(names.getCallsMetricName(), retry, (rt) -> rt.getMetrics().getNumberOfFailedCallsWithRetryAttempt()) + .tag(TagNames.NAME, retry.getName()) + .tag(TagNames.KIND, "failed_with_retry") + .register(registry).getId()); + + meterIdMap.put(retry.getName(), idSet); } /** Defines possible configuration for metric names. */ diff --git a/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/package-info.java b/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/package-info.java new file mode 100644 index 0000000000..29cacb2be8 --- /dev/null +++ b/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/package-info.java @@ -0,0 +1,24 @@ +/* + * + * Copyright 2018: Clint Checketts + * + * 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.micrometer.tagged; + +import io.github.resilience4j.core.lang.NonNullApi; +import io.github.resilience4j.core.lang.NonNullFields; \ No newline at end of file diff --git a/resilience4j-micrometer/src/test/java/io/github/resilience4j/micrometer/tagged/MetricsTestHelper.java b/resilience4j-micrometer/src/test/java/io/github/resilience4j/micrometer/tagged/MetricsTestHelper.java index 9c7a483e18..852e1bf30c 100644 --- a/resilience4j-micrometer/src/test/java/io/github/resilience4j/micrometer/tagged/MetricsTestHelper.java +++ b/resilience4j-micrometer/src/test/java/io/github/resilience4j/micrometer/tagged/MetricsTestHelper.java @@ -27,4 +27,10 @@ static Optional findGaugeByKindAndNameTags(Collection gauges, Stri .filter(g -> name.equals(g.getId().getTag(TagNames.NAME))) .findAny(); } + + static Optional findGaugeByNamesTag(Collection gauges, String name) { + return gauges.stream() + .filter(g -> name.equals(g.getId().getTag(TagNames.NAME))) + .findAny(); + } } diff --git a/resilience4j-micrometer/src/test/java/io/github/resilience4j/micrometer/tagged/TaggedBulkheadMetricsTest.java b/resilience4j-micrometer/src/test/java/io/github/resilience4j/micrometer/tagged/TaggedBulkheadMetricsTest.java index 6d51a43210..22ba9fcb05 100644 --- a/resilience4j-micrometer/src/test/java/io/github/resilience4j/micrometer/tagged/TaggedBulkheadMetricsTest.java +++ b/resilience4j-micrometer/src/test/java/io/github/resilience4j/micrometer/tagged/TaggedBulkheadMetricsTest.java @@ -16,19 +16,19 @@ package io.github.resilience4j.micrometer.tagged; import io.github.resilience4j.bulkhead.Bulkhead; +import io.github.resilience4j.bulkhead.BulkheadConfig; import io.github.resilience4j.bulkhead.BulkheadRegistry; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; - import org.junit.Before; import org.junit.Test; -import java.util.Arrays; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; +import static io.github.resilience4j.micrometer.tagged.MetricsTestHelper.findGaugeByNamesTag; import static io.github.resilience4j.micrometer.tagged.TaggedBulkheadMetrics.MetricNames.DEFAULT_BULKHEAD_AVAILABLE_CONCURRENT_CALLS_METRIC_NAME; import static io.github.resilience4j.micrometer.tagged.TaggedBulkheadMetrics.MetricNames.DEFAULT_BULKHEAD_MAX_ALLOWED_CONCURRENT_CALLS_METRIC_NAME; import static org.assertj.core.api.Assertions.assertThat; @@ -37,18 +37,74 @@ public class TaggedBulkheadMetricsTest { private MeterRegistry meterRegistry; private Bulkhead bulkhead; + private BulkheadRegistry bulkheadRegistry; + private TaggedBulkheadMetrics taggedBulkheadMetrics; @Before public void setUp() { meterRegistry = new SimpleMeterRegistry(); - BulkheadRegistry bulkheadRegistry = BulkheadRegistry.ofDefaults(); + bulkheadRegistry = BulkheadRegistry.ofDefaults(); bulkhead = bulkheadRegistry.bulkhead("backendA"); // record some basic stats bulkhead.tryObtainPermission(); bulkhead.tryObtainPermission(); - TaggedBulkheadMetrics.ofBulkheadRegistry(bulkheadRegistry).bindTo(meterRegistry); + taggedBulkheadMetrics = TaggedBulkheadMetrics.ofBulkheadRegistry(bulkheadRegistry); + taggedBulkheadMetrics.bindTo(meterRegistry); + } + + @Test + public void shouldAddMetricsForANewlyCreatedRetry() { + Bulkhead newBulkhead = bulkheadRegistry.bulkhead("backendB"); + + assertThat(taggedBulkheadMetrics.meterIdMap).containsKeys("backendA", "backendB"); + assertThat(taggedBulkheadMetrics.meterIdMap.get("backendA")).hasSize(2); + assertThat(taggedBulkheadMetrics.meterIdMap.get("backendB")).hasSize(2); + + List meters = meterRegistry.getMeters(); + assertThat(meters).hasSize(4); + + Collection gauges = meterRegistry.get(DEFAULT_BULKHEAD_MAX_ALLOWED_CONCURRENT_CALLS_METRIC_NAME).gauges(); + + Optional successful = findGaugeByNamesTag(gauges, newBulkhead.getName()); + assertThat(successful).isPresent(); + assertThat(successful.get().value()).isEqualTo(newBulkhead.getMetrics().getMaxAllowedConcurrentCalls()); + } + + @Test + public void shouldRemovedMetricsForRemovedRetry() { + List meters = meterRegistry.getMeters(); + assertThat(meters).hasSize(2); + + assertThat(taggedBulkheadMetrics.meterIdMap).containsKeys("backendA"); + bulkheadRegistry.remove("backendA"); + + assertThat(taggedBulkheadMetrics.meterIdMap).isEmpty(); + + meters = meterRegistry.getMeters(); + assertThat(meters).isEmpty(); + } + + @Test + public void shouldReplaceMetrics() { + Collection gauges = meterRegistry.get(DEFAULT_BULKHEAD_MAX_ALLOWED_CONCURRENT_CALLS_METRIC_NAME).gauges(); + + Optional successful = findGaugeByNamesTag(gauges, bulkhead.getName()); + assertThat(successful).isPresent(); + assertThat(successful.get().value()).isEqualTo(bulkhead.getMetrics().getMaxAllowedConcurrentCalls()); + + Bulkhead newBulkhead = Bulkhead.of(bulkhead.getName(), BulkheadConfig.custom() + .maxConcurrentCalls(100).build()); + + bulkheadRegistry.replace(bulkhead.getName(), newBulkhead); + + gauges = meterRegistry.get(DEFAULT_BULKHEAD_MAX_ALLOWED_CONCURRENT_CALLS_METRIC_NAME).gauges(); + + successful = findGaugeByNamesTag(gauges, newBulkhead.getName()); + assertThat(successful).isPresent(); + assertThat(successful.get().value()).isEqualTo(newBulkhead.getMetrics().getMaxAllowedConcurrentCalls()); + } @Test diff --git a/resilience4j-micrometer/src/test/java/io/github/resilience4j/micrometer/tagged/TaggedCircuitBreakerMetricsTest.java b/resilience4j-micrometer/src/test/java/io/github/resilience4j/micrometer/tagged/TaggedCircuitBreakerMetricsTest.java index 670c6b0a2e..2ae3bba9c1 100644 --- a/resilience4j-micrometer/src/test/java/io/github/resilience4j/micrometer/tagged/TaggedCircuitBreakerMetricsTest.java +++ b/resilience4j-micrometer/src/test/java/io/github/resilience4j/micrometer/tagged/TaggedCircuitBreakerMetricsTest.java @@ -16,48 +16,101 @@ package io.github.resilience4j.micrometer.tagged; import io.github.resilience4j.circuitbreaker.CircuitBreaker; +import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; - import org.junit.Before; import org.junit.Test; -import java.util.Arrays; -import java.util.Collection; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import static io.github.resilience4j.micrometer.tagged.MetricsTestHelper.findGaugeByKindAndNameTags; -import static io.github.resilience4j.micrometer.tagged.TaggedCircuitBreakerMetrics.MetricNames.DEFAULT_CIRCUIT_BREAKER_BUFFERED_CALLS; -import static io.github.resilience4j.micrometer.tagged.TaggedCircuitBreakerMetrics.MetricNames.DEFAULT_CIRCUIT_BREAKER_CALLS_METRIC_NAME; -import static io.github.resilience4j.micrometer.tagged.TaggedCircuitBreakerMetrics.MetricNames.DEFAULT_CIRCUIT_BREAKER_MAX_BUFFERED_CALLS; -import static io.github.resilience4j.micrometer.tagged.TaggedCircuitBreakerMetrics.MetricNames.DEFAULT_CIRCUIT_BREAKER_STATE_METRIC_NAME; +import static io.github.resilience4j.micrometer.tagged.TaggedCircuitBreakerMetrics.MetricNames.*; import static org.assertj.core.api.Assertions.assertThat; public class TaggedCircuitBreakerMetricsTest { private MeterRegistry meterRegistry; private CircuitBreaker circuitBreaker; + private CircuitBreakerRegistry circuitBreakerRegistry; + private TaggedCircuitBreakerMetrics taggedCircuitBreakerMetrics; @Before public void setUp() { meterRegistry = new SimpleMeterRegistry(); - CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults(); + circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults(); circuitBreaker = circuitBreakerRegistry.circuitBreaker("backendA"); // record some basic stats circuitBreaker.onError(0, new RuntimeException("oops")); circuitBreaker.onSuccess(0); - TaggedCircuitBreakerMetrics.ofCircuitBreakerRegistry(circuitBreakerRegistry).bindTo(meterRegistry); + taggedCircuitBreakerMetrics = TaggedCircuitBreakerMetrics.ofCircuitBreakerRegistry(circuitBreakerRegistry); + taggedCircuitBreakerMetrics.bindTo(meterRegistry); + } + + @Test + public void shouldAddMetricsForANewlyCreatedCircuitBreaker() { + CircuitBreaker newCircuitBreaker = circuitBreakerRegistry.circuitBreaker("backendB"); + newCircuitBreaker.onSuccess(0); + + assertThat(taggedCircuitBreakerMetrics.meterIdMap).containsKeys("backendA", "backendB"); + assertThat(taggedCircuitBreakerMetrics.meterIdMap.get("backendA")).hasSize(6); + assertThat(taggedCircuitBreakerMetrics.meterIdMap.get("backendB")).hasSize(6); + + List meters = meterRegistry.getMeters(); + assertThat(meters).hasSize(12); + + Collection gauges = meterRegistry.get(DEFAULT_CIRCUIT_BREAKER_CALLS_METRIC_NAME).gauges(); + + Optional successful = findGaugeByKindAndNameTags(gauges, "successful", newCircuitBreaker.getName()); + assertThat(successful).isPresent(); + assertThat(successful.get().value()).isEqualTo(newCircuitBreaker.getMetrics().getNumberOfSuccessfulCalls()); + } + + @Test + public void shouldRemovedMetricsForRemovedRetry() { + List meters = meterRegistry.getMeters(); + assertThat(meters).hasSize(6); + + assertThat(taggedCircuitBreakerMetrics.meterIdMap).containsKeys("backendA"); + circuitBreakerRegistry.remove("backendA"); + + assertThat(taggedCircuitBreakerMetrics.meterIdMap).isEmpty(); + + meters = meterRegistry.getMeters(); + assertThat(meters).isEmpty(); + } + + @Test + public void shouldReplaceMetrics() { + Gauge maxBuffered = meterRegistry.get(DEFAULT_CIRCUIT_BREAKER_MAX_BUFFERED_CALLS).gauge(); + + assertThat(maxBuffered).isNotNull(); + assertThat(maxBuffered.value()).isEqualTo((circuitBreaker.getMetrics().getMaxNumberOfBufferedCalls())); + assertThat(maxBuffered.getId().getTag(TagNames.NAME)).isEqualTo(circuitBreaker.getName()); + + CircuitBreaker newCircuitBreaker = CircuitBreaker.of(circuitBreaker.getName(), CircuitBreakerConfig.custom() + .ringBufferSizeInClosedState(1000).build()); + + circuitBreakerRegistry.replace(circuitBreaker.getName(), newCircuitBreaker); + + maxBuffered = meterRegistry.get(DEFAULT_CIRCUIT_BREAKER_MAX_BUFFERED_CALLS).gauge(); + + assertThat(maxBuffered).isNotNull(); + assertThat(maxBuffered.value()).isEqualTo(newCircuitBreaker.getMetrics().getMaxNumberOfBufferedCalls()); + assertThat(maxBuffered.getId().getTag(TagNames.NAME)).isEqualTo(newCircuitBreaker.getName()); } @Test public void notPermittedCallsGaugeReportsCorrespondingValue() { + List meters = meterRegistry.getMeters(); + assertThat(meters).hasSize(6); + Collection gauges = meterRegistry.get(DEFAULT_CIRCUIT_BREAKER_CALLS_METRIC_NAME).gauges(); Optional notPermitted = findGaugeByKindAndNameTags(gauges, "not_permitted", circuitBreaker.getName()); diff --git a/resilience4j-micrometer/src/test/java/io/github/resilience4j/micrometer/tagged/TaggedRateLimiterMetricsTest.java b/resilience4j-micrometer/src/test/java/io/github/resilience4j/micrometer/tagged/TaggedRateLimiterMetricsTest.java index 350e06a0f3..6e71f15071 100644 --- a/resilience4j-micrometer/src/test/java/io/github/resilience4j/micrometer/tagged/TaggedRateLimiterMetricsTest.java +++ b/resilience4j-micrometer/src/test/java/io/github/resilience4j/micrometer/tagged/TaggedRateLimiterMetricsTest.java @@ -16,19 +16,19 @@ package io.github.resilience4j.micrometer.tagged; import io.github.resilience4j.ratelimiter.RateLimiter; +import io.github.resilience4j.ratelimiter.RateLimiterConfig; import io.github.resilience4j.ratelimiter.RateLimiterRegistry; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; - import org.junit.Before; import org.junit.Test; -import java.util.Arrays; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; +import static io.github.resilience4j.micrometer.tagged.MetricsTestHelper.findGaugeByNamesTag; import static io.github.resilience4j.micrometer.tagged.TaggedRateLimiterMetrics.MetricNames.DEFAULT_AVAILABLE_PERMISSIONS_METRIC_NAME; import static io.github.resilience4j.micrometer.tagged.TaggedRateLimiterMetrics.MetricNames.DEFAULT_WAITING_THREADS_METRIC_NAME; import static org.assertj.core.api.Assertions.assertThat; @@ -37,14 +37,68 @@ public class TaggedRateLimiterMetricsTest { private MeterRegistry meterRegistry; private RateLimiter rateLimiter; + private RateLimiterRegistry rateLimiterRegistry; + private TaggedRateLimiterMetrics taggedRateLimiterMetrics; @Before public void setUp() { meterRegistry = new SimpleMeterRegistry(); - RateLimiterRegistry rateLimiterRegistry = RateLimiterRegistry.ofDefaults(); + rateLimiterRegistry = RateLimiterRegistry.ofDefaults(); rateLimiter = rateLimiterRegistry.rateLimiter("backendA"); - TaggedRateLimiterMetrics.ofRateLimiterRegistry(rateLimiterRegistry).bindTo(meterRegistry); + taggedRateLimiterMetrics = TaggedRateLimiterMetrics.ofRateLimiterRegistry(rateLimiterRegistry); + taggedRateLimiterMetrics.bindTo(meterRegistry); + } + + @Test + public void shouldAddMetricsForANewlyCreatedRateLimiter() { + RateLimiter newRateLimiter = rateLimiterRegistry.rateLimiter("backendB"); + + assertThat(taggedRateLimiterMetrics.meterIdMap).containsKeys("backendA", "backendB"); + assertThat(taggedRateLimiterMetrics.meterIdMap.get("backendA")).hasSize(2); + assertThat(taggedRateLimiterMetrics.meterIdMap.get("backendB")).hasSize(2); + + List meters = meterRegistry.getMeters(); + assertThat(meters).hasSize(4); + + Collection gauges = meterRegistry.get(DEFAULT_AVAILABLE_PERMISSIONS_METRIC_NAME).gauges(); + + Optional successful = findGaugeByNamesTag(gauges, newRateLimiter.getName()); + assertThat(successful).isPresent(); + assertThat(successful.get().value()).isEqualTo(newRateLimiter.getMetrics().getAvailablePermissions()); + } + + @Test + public void shouldRemovedMetricsForRemovedRetry() { + List meters = meterRegistry.getMeters(); + assertThat(meters).hasSize(2); + + assertThat(taggedRateLimiterMetrics.meterIdMap).containsKeys("backendA"); + rateLimiterRegistry.remove("backendA"); + + assertThat(taggedRateLimiterMetrics.meterIdMap).isEmpty(); + + meters = meterRegistry.getMeters(); + assertThat(meters).isEmpty(); + } + + @Test + public void shouldReplaceMetrics() { + Gauge availablePermissions = meterRegistry.get(DEFAULT_AVAILABLE_PERMISSIONS_METRIC_NAME).gauge(); + + assertThat(availablePermissions).isNotNull(); + assertThat(availablePermissions.value()).isEqualTo(rateLimiter.getMetrics().getAvailablePermissions()); + assertThat(availablePermissions.getId().getTag(TagNames.NAME)).isEqualTo(rateLimiter.getName()); + + RateLimiter newRateLimiter = RateLimiter.of(rateLimiter.getName(), RateLimiterConfig.custom().limitForPeriod(1000).build()); + + rateLimiterRegistry.replace(rateLimiter.getName(), newRateLimiter); + + availablePermissions = meterRegistry.get(DEFAULT_AVAILABLE_PERMISSIONS_METRIC_NAME).gauge(); + + assertThat(availablePermissions).isNotNull(); + assertThat(availablePermissions.value()).isEqualTo(newRateLimiter.getMetrics().getAvailablePermissions()); + assertThat(availablePermissions.getId().getTag(TagNames.NAME)).isEqualTo(newRateLimiter.getName()); } @Test diff --git a/resilience4j-micrometer/src/test/java/io/github/resilience4j/micrometer/tagged/TaggedRetryMetricsTest.java b/resilience4j-micrometer/src/test/java/io/github/resilience4j/micrometer/tagged/TaggedRetryMetricsTest.java index 6287d325f8..243660cb88 100644 --- a/resilience4j-micrometer/src/test/java/io/github/resilience4j/micrometer/tagged/TaggedRetryMetricsTest.java +++ b/resilience4j-micrometer/src/test/java/io/github/resilience4j/micrometer/tagged/TaggedRetryMetricsTest.java @@ -21,14 +21,10 @@ import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; - import org.junit.Before; import org.junit.Test; -import java.util.Collection; -import java.util.Collections; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import static io.github.resilience4j.micrometer.tagged.MetricsTestHelper.findGaugeByKindAndNameTags; @@ -39,17 +35,52 @@ public class TaggedRetryMetricsTest { private MeterRegistry meterRegistry; private Retry retry; + private RetryRegistry retryRegistry; + private TaggedRetryMetrics taggedRetryMetrics; @Before public void setUp() { meterRegistry = new SimpleMeterRegistry(); - RetryRegistry retryRegistry = RetryRegistry.ofDefaults(); + retryRegistry = RetryRegistry.ofDefaults(); retry = retryRegistry.retry("backendA"); // record some basic stats retry.executeRunnable(() -> {}); - TaggedRetryMetrics.ofRetryRegistry(retryRegistry).bindTo(meterRegistry); + taggedRetryMetrics = TaggedRetryMetrics.ofRetryRegistry(retryRegistry); + taggedRetryMetrics.bindTo(meterRegistry); + } + + @Test + public void shouldAddMetricsForANewlyCreatedRetry() { + Retry newRetry = retryRegistry.retry("backendB"); + + assertThat(taggedRetryMetrics.meterIdMap).containsKeys("backendA", "backendB"); + assertThat(taggedRetryMetrics.meterIdMap.get("backendA")).hasSize(4); + assertThat(taggedRetryMetrics.meterIdMap.get("backendB")).hasSize(4); + + List meters = meterRegistry.getMeters(); + assertThat(meters).hasSize(8); + + Collection gauges = meterRegistry.get(DEFAULT_RETRY_CALLS).gauges(); + + Optional successfulWithoutRetry = findGaugeByKindAndNameTags(gauges, "successful_without_retry", newRetry.getName()); + assertThat(successfulWithoutRetry).isPresent(); + assertThat(successfulWithoutRetry.get().value()).isEqualTo(newRetry.getMetrics().getNumberOfSuccessfulCallsWithoutRetryAttempt()); + } + + @Test + public void shouldRemovedMetricsForRemovedRetry() { + List meters = meterRegistry.getMeters(); + assertThat(meters).hasSize(4); + + assertThat(taggedRetryMetrics.meterIdMap).containsKeys("backendA"); + retryRegistry.remove("backendA"); + + assertThat(taggedRetryMetrics.meterIdMap).isEmpty(); + + meters = meterRegistry.getMeters(); + assertThat(meters).isEmpty(); } @Test diff --git a/resilience4j-ratelimiter/src/main/java/io/github/resilience4j/ratelimiter/internal/InMemoryRateLimiterRegistry.java b/resilience4j-ratelimiter/src/main/java/io/github/resilience4j/ratelimiter/internal/InMemoryRateLimiterRegistry.java index 7340031a79..19003383e0 100644 --- a/resilience4j-ratelimiter/src/main/java/io/github/resilience4j/ratelimiter/internal/InMemoryRateLimiterRegistry.java +++ b/resilience4j-ratelimiter/src/main/java/io/github/resilience4j/ratelimiter/internal/InMemoryRateLimiterRegistry.java @@ -18,7 +18,7 @@ */ package io.github.resilience4j.ratelimiter.internal; -import io.github.resilience4j.core.AbstractRegistry; +import io.github.resilience4j.core.registry.AbstractRegistry; import io.github.resilience4j.core.ConfigurationNotFoundException; import io.github.resilience4j.ratelimiter.RateLimiter; import io.github.resilience4j.ratelimiter.RateLimiterConfig; @@ -62,7 +62,7 @@ public InMemoryRateLimiterRegistry(RateLimiterConfig defaultConfig) { */ @Override public Seq getAllRateLimiters() { - return Array.ofAll(targetMap.values()); + return Array.ofAll(entryMap.values()); } /** diff --git a/resilience4j-ratelimiter/src/main/java/io/github/resilience4j/ratelimiter/internal/RateLimiterEventProcessor.java b/resilience4j-ratelimiter/src/main/java/io/github/resilience4j/ratelimiter/internal/RateLimiterEventProcessor.java index 78f7035344..e561fd7d7d 100644 --- a/resilience4j-ratelimiter/src/main/java/io/github/resilience4j/ratelimiter/internal/RateLimiterEventProcessor.java +++ b/resilience4j-ratelimiter/src/main/java/io/github/resilience4j/ratelimiter/internal/RateLimiterEventProcessor.java @@ -34,13 +34,13 @@ public void consumeEvent(RateLimiterEvent event) { @Override public RateLimiter.EventPublisher onSuccess(EventConsumer onSuccessEventConsumer) { - registerConsumer(RateLimiterOnSuccessEvent.class, onSuccessEventConsumer); + registerConsumer(RateLimiterOnSuccessEvent.class.getSimpleName(), onSuccessEventConsumer); return this; } @Override public RateLimiter.EventPublisher onFailure(EventConsumer onOnFailureEventConsumer) { - registerConsumer(RateLimiterOnFailureEvent.class, onOnFailureEventConsumer); + registerConsumer(RateLimiterOnFailureEvent.class.getSimpleName(), onOnFailureEventConsumer); return this; } } \ No newline at end of file diff --git a/resilience4j-ratelimiter/src/test/java/io/github/resilience4j/ratelimiter/internal/InMemoryRateLimiterRegistryTest.java b/resilience4j-ratelimiter/src/test/java/io/github/resilience4j/ratelimiter/internal/InMemoryRateLimiterRegistryTest.java index e6ea801031..7e77144586 100644 --- a/resilience4j-ratelimiter/src/test/java/io/github/resilience4j/ratelimiter/internal/InMemoryRateLimiterRegistryTest.java +++ b/resilience4j-ratelimiter/src/test/java/io/github/resilience4j/ratelimiter/internal/InMemoryRateLimiterRegistryTest.java @@ -25,11 +25,8 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.mockito.BDDMockito; -import org.slf4j.Logger; import java.time.Duration; -import java.util.function.Consumer; import java.util.function.Supplier; import static org.assertj.core.api.BDDAssertions.then; @@ -46,12 +43,10 @@ public class InMemoryRateLimiterRegistryTest { @Rule public ExpectedException exception = ExpectedException.none(); private RateLimiterConfig config; - private Logger LOGGER; @Before public void init() { - LOGGER = mock(Logger.class); config = RateLimiterConfig.custom() .timeoutDuration(TIMEOUT) .limitRefreshPeriod(REFRESH_PERIOD) @@ -147,34 +142,4 @@ public void rateLimiterGetAllRateLimiters() { assertThat(registry.getAllRateLimiters().get(0).getName()).isEqualTo("foo"); } - @Test - public void rateLimiterTestPostConsumer() { - RateLimiterRegistry registry = new InMemoryRateLimiterRegistry(config); - Consumer consumer1 = circuitBreaker -> LOGGER.info("invoking the post consumer1"); - registry.registerPostCreationConsumer(consumer1); - registry.rateLimiter("foo"); - BDDMockito.then(LOGGER).should(times(1)).info("invoking the post consumer1"); - - } - - @Test - public void rateLimiterTestPostConsumerWithCustomConfigSypplier() { - RateLimiterRegistry registry = new InMemoryRateLimiterRegistry(config); - Consumer consumer1 = circuitBreaker -> LOGGER.info("invoking the post consumer1"); - registry.registerPostCreationConsumer(consumer1); - registry.rateLimiter("foo", () -> RateLimiterConfig.custom().build()); - BDDMockito.then(LOGGER).should(times(1)).info("invoking the post consumer1"); - - } - - @Test - public void rateLimiterTestPostConsumerWithCustomConfig() { - RateLimiterRegistry registry = new InMemoryRateLimiterRegistry(config); - Consumer consumer1 = circuitBreaker -> LOGGER.info("invoking the post consumer1"); - registry.registerPostCreationConsumer(consumer1); - registry.rateLimiter("foo", RateLimiterConfig.custom().build()); - BDDMockito.then(LOGGER).should(times(1)).info("invoking the post consumer1"); - - } - } \ No newline at end of file diff --git a/resilience4j-retry/src/main/java/io/github/resilience4j/retry/internal/AsyncRetryImpl.java b/resilience4j-retry/src/main/java/io/github/resilience4j/retry/internal/AsyncRetryImpl.java index 4cfe0f2eae..fbe5b68c70 100644 --- a/resilience4j-retry/src/main/java/io/github/resilience4j/retry/internal/AsyncRetryImpl.java +++ b/resilience4j-retry/src/main/java/io/github/resilience4j/retry/internal/AsyncRetryImpl.java @@ -18,6 +18,13 @@ */ package io.github.resilience4j.retry.internal; +import io.github.resilience4j.core.EventConsumer; +import io.github.resilience4j.core.EventProcessor; +import io.github.resilience4j.core.lang.Nullable; +import io.github.resilience4j.retry.AsyncRetry; +import io.github.resilience4j.retry.RetryConfig; +import io.github.resilience4j.retry.event.*; + import java.util.concurrent.CompletionException; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @@ -26,17 +33,6 @@ import java.util.function.Predicate; import java.util.function.Supplier; -import io.github.resilience4j.core.EventConsumer; -import io.github.resilience4j.core.EventProcessor; -import io.github.resilience4j.core.lang.Nullable; -import io.github.resilience4j.retry.AsyncRetry; -import io.github.resilience4j.retry.RetryConfig; -import io.github.resilience4j.retry.event.RetryEvent; -import io.github.resilience4j.retry.event.RetryOnErrorEvent; -import io.github.resilience4j.retry.event.RetryOnIgnoredErrorEvent; -import io.github.resilience4j.retry.event.RetryOnRetryEvent; -import io.github.resilience4j.retry.event.RetryOnSuccessEvent; - // * @deprecated replaced by @see io.github.resilience4j.retry.Retry#decorateCompletionStage() @Deprecated public class AsyncRetryImpl implements AsyncRetry { @@ -204,25 +200,25 @@ public void consumeEvent(RetryEvent event) { @Override public EventPublisher onRetry(EventConsumer onRetryEventConsumer) { - registerConsumer(RetryOnRetryEvent.class, onRetryEventConsumer); + registerConsumer(RetryOnRetryEvent.class.getSimpleName(), onRetryEventConsumer); return this; } @Override public AsyncRetry.EventPublisher onSuccess(EventConsumer onSuccessEventConsumer) { - registerConsumer(RetryOnSuccessEvent.class, onSuccessEventConsumer); + registerConsumer(RetryOnSuccessEvent.class.getSimpleName(), onSuccessEventConsumer); return this; } @Override public AsyncRetry.EventPublisher onError(EventConsumer onErrorEventConsumer) { - registerConsumer(RetryOnErrorEvent.class, onErrorEventConsumer); + registerConsumer(RetryOnErrorEvent.class.getSimpleName(), onErrorEventConsumer); return this; } @Override public AsyncRetry.EventPublisher onIgnoredError(EventConsumer onIgnoredErrorEventConsumer) { - registerConsumer(RetryOnIgnoredErrorEvent.class, onIgnoredErrorEventConsumer); + registerConsumer(RetryOnIgnoredErrorEvent.class.getSimpleName(), onIgnoredErrorEventConsumer); return this; } } diff --git a/resilience4j-retry/src/main/java/io/github/resilience4j/retry/internal/InMemoryRetryRegistry.java b/resilience4j-retry/src/main/java/io/github/resilience4j/retry/internal/InMemoryRetryRegistry.java index d92203d9c0..3779578a76 100644 --- a/resilience4j-retry/src/main/java/io/github/resilience4j/retry/internal/InMemoryRetryRegistry.java +++ b/resilience4j-retry/src/main/java/io/github/resilience4j/retry/internal/InMemoryRetryRegistry.java @@ -15,7 +15,7 @@ */ package io.github.resilience4j.retry.internal; -import io.github.resilience4j.core.AbstractRegistry; +import io.github.resilience4j.core.registry.AbstractRegistry; import io.github.resilience4j.core.ConfigurationNotFoundException; import io.github.resilience4j.retry.Retry; import io.github.resilience4j.retry.RetryConfig; @@ -59,7 +59,7 @@ public InMemoryRetryRegistry(RetryConfig defaultConfig) { */ @Override public Seq getAllRetries() { - return Array.ofAll(targetMap.values()); + return Array.ofAll(entryMap.values()); } /** diff --git a/resilience4j-retry/src/main/java/io/github/resilience4j/retry/internal/RetryImpl.java b/resilience4j-retry/src/main/java/io/github/resilience4j/retry/internal/RetryImpl.java index 18bba4e0c7..8f4d95d91b 100644 --- a/resilience4j-retry/src/main/java/io/github/resilience4j/retry/internal/RetryImpl.java +++ b/resilience4j-retry/src/main/java/io/github/resilience4j/retry/internal/RetryImpl.java @@ -18,28 +18,24 @@ */ package io.github.resilience4j.retry.internal; -import java.util.concurrent.CompletionException; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.atomic.LongAdder; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; - import io.github.resilience4j.core.EventConsumer; import io.github.resilience4j.core.EventProcessor; import io.github.resilience4j.core.lang.Nullable; import io.github.resilience4j.retry.Retry; import io.github.resilience4j.retry.RetryConfig; -import io.github.resilience4j.retry.event.RetryEvent; -import io.github.resilience4j.retry.event.RetryOnErrorEvent; -import io.github.resilience4j.retry.event.RetryOnIgnoredErrorEvent; -import io.github.resilience4j.retry.event.RetryOnRetryEvent; -import io.github.resilience4j.retry.event.RetryOnSuccessEvent; +import io.github.resilience4j.retry.event.*; import io.vavr.CheckedConsumer; import io.vavr.control.Option; import io.vavr.control.Try; +import java.util.concurrent.CompletionException; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.atomic.LongAdder; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + public class RetryImpl implements Retry { @@ -308,25 +304,25 @@ public void consumeEvent(RetryEvent event) { @Override public EventPublisher onRetry(EventConsumer onRetryEventConsumer) { - registerConsumer(RetryOnRetryEvent.class, onRetryEventConsumer); + registerConsumer(RetryOnRetryEvent.class.getSimpleName(), onRetryEventConsumer); return this; } @Override public EventPublisher onSuccess(EventConsumer onSuccessEventConsumer) { - registerConsumer(RetryOnSuccessEvent.class, onSuccessEventConsumer); + registerConsumer(RetryOnSuccessEvent.class.getSimpleName(), onSuccessEventConsumer); return this; } @Override public EventPublisher onError(EventConsumer onErrorEventConsumer) { - registerConsumer(RetryOnErrorEvent.class, onErrorEventConsumer); + registerConsumer(RetryOnErrorEvent.class.getSimpleName(), onErrorEventConsumer); return this; } @Override public EventPublisher onIgnoredError(EventConsumer onIgnoredErrorEventConsumer) { - registerConsumer(RetryOnIgnoredErrorEvent.class, onIgnoredErrorEventConsumer); + registerConsumer(RetryOnIgnoredErrorEvent.class.getSimpleName(), onIgnoredErrorEventConsumer); return this; } } diff --git a/resilience4j-retry/src/test/java/io/github/resilience4j/retry/RetryRegistryTest.java b/resilience4j-retry/src/test/java/io/github/resilience4j/retry/RetryRegistryTest.java index 200e3e5e00..33748a7d3f 100644 --- a/resilience4j-retry/src/test/java/io/github/resilience4j/retry/RetryRegistryTest.java +++ b/resilience4j-retry/src/test/java/io/github/resilience4j/retry/RetryRegistryTest.java @@ -19,31 +19,22 @@ import org.assertj.core.api.Assertions; import org.junit.Before; import org.junit.Test; -import org.mockito.BDDMockito; -import org.slf4j.Logger; import java.time.Duration; import java.util.HashMap; import java.util.Map; -import java.util.function.Consumer; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; public class RetryRegistryTest { private RetryRegistry retryRegistry; - private Logger LOGGER; - private Consumer post_consumer = circuitBreaker -> LOGGER.info("invoking the post consumer1"); @Before public void setUp() { - LOGGER = mock(Logger.class); retryRegistry = RetryRegistry.ofDefaults(); - retryRegistry.registerPostCreationConsumer(post_consumer); } @Test @@ -56,7 +47,6 @@ public void shouldReturnTheCorrectName() { Retry retry = retryRegistry.retry("testName"); Assertions.assertThat(retry).isNotNull(); Assertions.assertThat(retry.getName()).isEqualTo("testName"); - BDDMockito.then(LOGGER).should(times(1)).info("invoking the post consumer1"); } @Test @@ -65,7 +55,6 @@ public void shouldBeTheSameRetry() { Retry retry2 = retryRegistry.retry("testName"); Assertions.assertThat(retry).isSameAs(retry2); Assertions.assertThat(retryRegistry.getAllRetries()).hasSize(1); - BDDMockito.then(LOGGER).should(times(1)).info("invoking the post consumer1"); } @Test @@ -75,7 +64,6 @@ public void shouldBeNotTheSameRetry() { Retry retry2 = retryRegistry.retry("otherTestName"); Assertions.assertThat(retry).isNotSameAs(retry2); Assertions.assertThat(retryRegistry.getAllRetries()).hasSize(2); - BDDMockito.then(LOGGER).should(times(2)).info("invoking the post consumer1"); } @Test @@ -84,7 +72,6 @@ public void canBuildRetryFromRegistryWithConfig() { Retry retry = retryRegistry.retry("testName", config); Assertions.assertThat(retry).isNotNull(); Assertions.assertThat(retryRegistry.getAllRetries()).hasSize(1); - BDDMockito.then(LOGGER).should(times(1)).info("invoking the post consumer1"); } @Test @@ -93,18 +80,15 @@ public void canBuildRetryFromRegistryWithConfigSupplier() { Retry retry = retryRegistry.retry("testName", () -> config); Assertions.assertThat(retry).isNotNull(); Assertions.assertThat(retryRegistry.getAllRetries()).hasSize(1); - BDDMockito.then(LOGGER).should(times(1)).info("invoking the post consumer1"); } @Test public void canBuildRetryRegistryWithConfig() { RetryConfig config = RetryConfig.custom().maxAttempts(1000).waitDuration(Duration.ofSeconds(300)).build(); retryRegistry = RetryRegistry.of(config); - retryRegistry.registerPostCreationConsumer(post_consumer); Retry retry = retryRegistry.retry("testName", () -> config); Assertions.assertThat(retry).isNotNull(); Assertions.assertThat(retryRegistry.getAllRetries()).hasSize(1); - BDDMockito.then(LOGGER).should(times(1)).info("invoking the post consumer1"); } @Test diff --git a/resilience4j-spring-boot-common/src/main/java/io/github/resilience4j/springboot/common/circuitbreaker/autoconfigure/AbstractCircuitBreakerConfigurationOnMissingBean.java b/resilience4j-spring-boot-common/src/main/java/io/github/resilience4j/springboot/common/circuitbreaker/autoconfigure/AbstractCircuitBreakerConfigurationOnMissingBean.java index f5225e4e5a..e3859735f0 100644 --- a/resilience4j-spring-boot-common/src/main/java/io/github/resilience4j/springboot/common/circuitbreaker/autoconfigure/AbstractCircuitBreakerConfigurationOnMissingBean.java +++ b/resilience4j-spring-boot-common/src/main/java/io/github/resilience4j/springboot/common/circuitbreaker/autoconfigure/AbstractCircuitBreakerConfigurationOnMissingBean.java @@ -15,26 +15,23 @@ */ package io.github.resilience4j.springboot.common.circuitbreaker.autoconfigure; -import java.util.List; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Conditional; -import org.springframework.context.annotation.Configuration; - import io.github.resilience4j.circuitbreaker.CircuitBreaker; +import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; -import io.github.resilience4j.circuitbreaker.configure.CircuitBreakerAspect; -import io.github.resilience4j.circuitbreaker.configure.CircuitBreakerAspectExt; -import io.github.resilience4j.circuitbreaker.configure.CircuitBreakerConfiguration; -import io.github.resilience4j.circuitbreaker.configure.CircuitBreakerConfigurationProperties; -import io.github.resilience4j.circuitbreaker.configure.ReactorCircuitBreakerAspectExt; -import io.github.resilience4j.circuitbreaker.configure.RxJava2CircuitBreakerAspectExt; +import io.github.resilience4j.circuitbreaker.configure.*; import io.github.resilience4j.circuitbreaker.event.CircuitBreakerEvent; import io.github.resilience4j.consumer.EventConsumerRegistry; import io.github.resilience4j.utils.ReactorOnClasspathCondition; import io.github.resilience4j.utils.RxJava2OnClasspathCondition; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; @Configuration public abstract class AbstractCircuitBreakerConfigurationOnMissingBean { @@ -50,13 +47,17 @@ public AbstractCircuitBreakerConfigurationOnMissingBean(CircuitBreakerConfigurat @Bean @ConditionalOnMissingBean public CircuitBreakerRegistry circuitBreakerRegistry(EventConsumerRegistry eventConsumerRegistry) { - CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults(); + Map configs = circuitBreakerProperties.getConfigs() + .entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, + entry -> circuitBreakerProperties.createCircuitBreakerConfig(entry.getValue()))); + + CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.of(configs); // Register the event consumers - circuitBreakerConfiguration.registerPostCreationEventConsumer(circuitBreakerRegistry, eventConsumerRegistry); + circuitBreakerConfiguration.registerEventConsumer(circuitBreakerRegistry, eventConsumerRegistry); // Register a consumer to hook up any health indicators for circuit breakers after creation. This will catch ones that get // created beyond initially configured backends. - circuitBreakerRegistry.registerPostCreationConsumer(this::createHeathIndicatorForCircuitBreaker); + circuitBreakerRegistry.getEventPublisher().onEntryAdded(event -> createHealthIndicatorForCircuitBreaker(event.getAddedEntry())); // Initialize backends that were initially configured. circuitBreakerConfiguration.initializeBackends(circuitBreakerRegistry); @@ -64,7 +65,7 @@ public CircuitBreakerRegistry circuitBreakerRegistry(EventConsumerRegistry eventConsumerRegistry() { return circuitBreakerConfiguration.eventConsumerRegistry(); } - protected void createHeathIndicatorForCircuitBreaker(CircuitBreaker circuitBreaker) { + protected void createHealthIndicatorForCircuitBreaker(CircuitBreaker circuitBreaker) { BackendProperties backendProperties = circuitBreakerProperties.findCircuitBreakerBackend(circuitBreaker, circuitBreaker.getCircuitBreakerConfig()); if (backendProperties != null && backendProperties.getRegisterHealthIndicator()) { diff --git a/resilience4j-spring-boot/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationTest.java b/resilience4j-spring-boot/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationTest.java index 6f0003cd7d..537ac378e7 100644 --- a/resilience4j-spring-boot/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationTest.java +++ b/resilience4j-spring-boot/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationTest.java @@ -15,12 +15,13 @@ */ package io.github.resilience4j.circuitbreaker; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertEquals; - -import java.io.IOException; -import java.time.Duration; - +import io.github.resilience4j.circuitbreaker.autoconfigure.CircuitBreakerProperties; +import io.github.resilience4j.circuitbreaker.configure.CircuitBreakerAspect; +import io.github.resilience4j.circuitbreaker.monitoring.endpoint.CircuitBreakerEndpointResponse; +import io.github.resilience4j.circuitbreaker.monitoring.endpoint.CircuitBreakerEventsEndpointResponse; +import io.github.resilience4j.service.test.DummyService; +import io.github.resilience4j.service.test.TestApplication; +import io.prometheus.client.CollectorRegistry; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; @@ -34,13 +35,11 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import io.github.resilience4j.circuitbreaker.autoconfigure.CircuitBreakerProperties; -import io.github.resilience4j.circuitbreaker.configure.CircuitBreakerAspect; -import io.github.resilience4j.circuitbreaker.monitoring.endpoint.CircuitBreakerEndpointResponse; -import io.github.resilience4j.circuitbreaker.monitoring.endpoint.CircuitBreakerEventsEndpointResponse; -import io.github.resilience4j.service.test.DummyService; -import io.github.resilience4j.service.test.TestApplication; -import io.prometheus.client.CollectorRegistry; +import java.io.IOException; +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, @@ -113,7 +112,6 @@ public void testCircuitBreakerAutoConfiguration() throws IOException { ResponseEntity circuitBreakerList = restTemplate.getForEntity("/circuitbreaker", CircuitBreakerEndpointResponse.class); assertThat(circuitBreakerList.getBody().getCircuitBreakers()).hasSize(5).containsExactly("backendA", "backendB", "backendSharedA", "backendSharedB", "backendSharedC"); - ResponseEntity circuitBreakerEventList = restTemplate.getForEntity("/circuitbreaker/events", CircuitBreakerEventsEndpointResponse.class); assertThat(circuitBreakerEventList.getBody().getCircuitBreakerEvents()).hasSize(2); diff --git a/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerConfigurationOnMissingBean.java b/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerConfigurationOnMissingBean.java index 2fa9913bce..afa21f089b 100644 --- a/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerConfigurationOnMissingBean.java +++ b/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerConfigurationOnMissingBean.java @@ -15,9 +15,13 @@ */ package io.github.resilience4j.circuitbreaker.autoconfigure; -import java.util.Map; - -import org.springframework.beans.BeansException; +import io.github.resilience4j.circuitbreaker.CircuitBreaker; +import io.github.resilience4j.circuitbreaker.configure.CircuitBreakerConfigurationProperties; +import io.github.resilience4j.circuitbreaker.configure.CircuitBreakerConfigurationProperties.BackendProperties; +import io.github.resilience4j.circuitbreaker.event.CircuitBreakerEvent; +import io.github.resilience4j.circuitbreaker.monitoring.health.CircuitBreakerHealthIndicator; +import io.github.resilience4j.consumer.EventConsumerRegistry; +import io.github.resilience4j.springboot.common.circuitbreaker.autoconfigure.AbstractCircuitBreakerConfigurationOnMissingBean; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.boot.actuate.health.HealthIndicatorRegistry; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -26,13 +30,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import io.github.resilience4j.circuitbreaker.CircuitBreaker; -import io.github.resilience4j.circuitbreaker.configure.CircuitBreakerConfigurationProperties; -import io.github.resilience4j.circuitbreaker.configure.CircuitBreakerConfigurationProperties.BackendProperties; -import io.github.resilience4j.circuitbreaker.event.CircuitBreakerEvent; -import io.github.resilience4j.circuitbreaker.monitoring.health.CircuitBreakerHealthIndicator; -import io.github.resilience4j.consumer.EventConsumerRegistry; -import io.github.resilience4j.springboot.common.circuitbreaker.autoconfigure.AbstractCircuitBreakerConfigurationOnMissingBean; +import java.util.Map; @Configuration public class CircuitBreakerConfigurationOnMissingBean extends AbstractCircuitBreakerConfigurationOnMissingBean implements ApplicationContextAware { @@ -59,7 +57,7 @@ public EventConsumerRegistry eventConsumerRegistry() { } @Override - protected void createHeathIndicatorForCircuitBreaker(CircuitBreaker circuitBreaker) { + protected void createHealthIndicatorForCircuitBreaker(CircuitBreaker circuitBreaker) { BackendProperties backendProperties = circuitBreakerProperties.findCircuitBreakerBackend(circuitBreaker, circuitBreaker.getCircuitBreakerConfig()); if (backendProperties != null && backendProperties.getRegisterHealthIndicator()) { diff --git a/resilience4j-spring/src/main/java/io/github/resilience4j/circuitbreaker/configure/CircuitBreakerConfiguration.java b/resilience4j-spring/src/main/java/io/github/resilience4j/circuitbreaker/configure/CircuitBreakerConfiguration.java index 309cf220c2..4f942b6f21 100644 --- a/resilience4j-spring/src/main/java/io/github/resilience4j/circuitbreaker/configure/CircuitBreakerConfiguration.java +++ b/resilience4j-spring/src/main/java/io/github/resilience4j/circuitbreaker/configure/CircuitBreakerConfiguration.java @@ -15,13 +15,6 @@ */ package io.github.resilience4j.circuitbreaker.configure; -import java.util.List; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Conditional; -import org.springframework.context.annotation.Configuration; - import io.github.resilience4j.circuitbreaker.CircuitBreaker; import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; @@ -31,6 +24,14 @@ import io.github.resilience4j.consumer.EventConsumerRegistry; import io.github.resilience4j.utils.ReactorOnClasspathCondition; import io.github.resilience4j.utils.RxJava2OnClasspathCondition; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * {@link org.springframework.context.annotation.Configuration @@ -47,8 +48,12 @@ public CircuitBreakerConfiguration(CircuitBreakerConfigurationProperties circuit @Bean public CircuitBreakerRegistry circuitBreakerRegistry(EventConsumerRegistry eventConsumerRegistry) { - CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults(); - registerPostCreationEventConsumer(circuitBreakerRegistry, eventConsumerRegistry); + Map configs = circuitBreakerProperties.getConfigs() + .entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, + entry -> circuitBreakerProperties.createCircuitBreakerConfig(entry.getValue()))); + + CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.of(configs); + registerEventConsumer(circuitBreakerRegistry, eventConsumerRegistry); initializeBackends(circuitBreakerRegistry); return circuitBreakerRegistry; } @@ -106,10 +111,10 @@ public void initializeBackends(CircuitBreakerRegistry circuitBreakerRegistry) { * @param circuitBreakerRegistry The circuit breaker registry. * @param eventConsumerRegistry The event consumer registry. */ - public void registerPostCreationEventConsumer(CircuitBreakerRegistry circuitBreakerRegistry, - EventConsumerRegistry eventConsumerRegistry) { + public void registerEventConsumer(CircuitBreakerRegistry circuitBreakerRegistry, + EventConsumerRegistry eventConsumerRegistry) { final EventConsumerRegister eventConsumerRegister = new EventConsumerRegister(eventConsumerRegistry); - circuitBreakerRegistry.registerPostCreationConsumer(eventConsumerRegister::registerEventConsumer); + circuitBreakerRegistry.getEventPublisher().onEntryAdded(event -> eventConsumerRegister.registerEventConsumer(event.getAddedEntry())); } /** diff --git a/resilience4j-spring/src/main/java/io/github/resilience4j/circuitbreaker/configure/CircuitBreakerConfigurationProperties.java b/resilience4j-spring/src/main/java/io/github/resilience4j/circuitbreaker/configure/CircuitBreakerConfigurationProperties.java index f8d046180c..083aefc8d3 100644 --- a/resilience4j-spring/src/main/java/io/github/resilience4j/circuitbreaker/configure/CircuitBreakerConfigurationProperties.java +++ b/resilience4j-spring/src/main/java/io/github/resilience4j/circuitbreaker/configure/CircuitBreakerConfigurationProperties.java @@ -15,25 +15,23 @@ * limitations under the License. */ -import java.time.Duration; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Predicate; - -import javax.validation.constraints.Max; -import javax.validation.constraints.Min; -import javax.validation.constraints.NotNull; - -import org.hibernate.validator.constraints.time.DurationMin; -import org.springframework.beans.BeanUtils; -import org.springframework.context.annotation.Configuration; -import org.springframework.util.StringUtils; - import io.github.resilience4j.circuitbreaker.CircuitBreaker; import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig.Builder; import io.github.resilience4j.core.lang.Nullable; import io.github.resilience4j.utils.CommonUtils; +import org.hibernate.validator.constraints.time.DurationMin; +import org.springframework.beans.BeanUtils; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.StringUtils; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Predicate; @Configuration public class CircuitBreakerConfigurationProperties { @@ -80,7 +78,7 @@ public CircuitBreakerConfig createCircuitBreakerConfigFrom(String baseConfigName return createCircuitBreakerConfig(getConfigProperties(baseConfigName)); } - private CircuitBreakerConfig createCircuitBreakerConfig(@Nullable BackendProperties backendProperties) { + public CircuitBreakerConfig createCircuitBreakerConfig(@Nullable BackendProperties backendProperties) { return buildCircuitBreakerConfig(backendProperties).build(); } diff --git a/resilience4j-spring/src/test/java/io/github/resilience4j/circuitbreaker/configure/CircuitBreakerConfigurationTest.java b/resilience4j-spring/src/test/java/io/github/resilience4j/circuitbreaker/configure/CircuitBreakerConfigurationTest.java index 18d39b3790..44270b8754 100644 --- a/resilience4j-spring/src/test/java/io/github/resilience4j/circuitbreaker/configure/CircuitBreakerConfigurationTest.java +++ b/resilience4j-spring/src/test/java/io/github/resilience4j/circuitbreaker/configure/CircuitBreakerConfigurationTest.java @@ -1,19 +1,17 @@ package io.github.resilience4j.circuitbreaker.configure; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.when; - -import java.util.Collections; - +import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; +import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; +import io.github.resilience4j.consumer.DefaultEventConsumerRegistry; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; -import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; -import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; -import io.github.resilience4j.consumer.DefaultEventConsumerRegistry; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; /** * test custom init of circuit breaker registry @@ -30,7 +28,7 @@ public void testCircuitBreakerRegistryConfig() { CircuitBreakerConfigurationProperties.BackendProperties backendProperties = new CircuitBreakerConfigurationProperties.BackendProperties(); backendProperties.setFailureRateThreshold(3); when(circuitBreakerConfigurationProperties.getBackends()).thenReturn(Collections.singletonMap("testBackend", backendProperties)); - when(circuitBreakerConfigurationProperties.createCircuitBreakerConfig(anyString())).thenReturn(CircuitBreakerConfig.ofDefaults()); + when(circuitBreakerConfigurationProperties.createCircuitBreakerConfig("testBackend")).thenReturn(CircuitBreakerConfig.ofDefaults()); CircuitBreakerConfiguration circuitBreakerConfiguration = new CircuitBreakerConfiguration(circuitBreakerConfigurationProperties); CircuitBreakerRegistry circuitBreakerRegistry = circuitBreakerConfiguration.circuitBreakerRegistry(new DefaultEventConsumerRegistry<>());