Skip to content

Commit

Permalink
Refactor Resilience4jTemplate
Browse files Browse the repository at this point in the history
  • Loading branch information
mercyblitz committed Jan 25, 2025
1 parent b603ccc commit 49f0322
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,30 +38,30 @@
public class Resilience4jContext<E> {

/**
* The name of Resilience4j's entry
* The name of Resilience4j's entry.
*/
private final String entryName;

/**
* The Resilience4j's entry, e.g., {@link CircuitBreaker}
* The Resilience4j's entry, e.g., {@link CircuitBreaker}.
*/
@NonNull
private final E entry;

/**
* The start time of the execution
* The start time of the execution.
*/
@Nullable
private Long startTime;

/**
* The execution result
* The execution result, if <code>null</code>, it means the execution does not return any value.
*/
@Nullable
Object result;

/**
* The optional {@link Throwable} instance, if <code>null</code>, it means the execution is successful
* The optional {@link Throwable} instance, if <code>null</code>, it means the execution is successful.
*/
@Nullable
Throwable failure;
Expand All @@ -72,15 +72,15 @@ public class Resilience4jContext<E> {
@Nullable
private Map<String, Object> attributes;

Resilience4jContext(String entryName, E entry) {
protected Resilience4jContext(String entryName, E entry) {
assertNotNull(entryName, "The entry name must not be null.");
assertNotNull(entry, "The entry must not be null.");
this.entryName = entryName;
this.entry = entry;
}

/**
* Get the name of Resilience4j's entry
* Get the name of Resilience4j's entry.
*
* @return non-null
*/
Expand All @@ -98,7 +98,7 @@ public E getEntry() {
}

/**
* Set the start time of the execution
* Set the start time of the execution.
*
* @param startTime the start time of the execution
*/
Expand All @@ -107,7 +107,7 @@ public void setStartTime(Long startTime) {
}

/**
* Get the start time of the execution
* Get the start time of the execution.
*
* @return <code>null</code> if {@link #setStartTime(Long)} method will be invoked
*/
Expand All @@ -117,7 +117,7 @@ public Long getStartTime() {
}

/**
* Set the result of the execution
* Set the result of the execution.
*
* @return <code>null</code> if the target callback does not return value or is failed
*/
Expand All @@ -127,7 +127,7 @@ public Object getResult() {
}

/**
* Set the failure of the execution
* Set the failure of the execution.
*
* @return <code>null</code> if the target callback executes successfully
*/
Expand Down Expand Up @@ -195,7 +195,7 @@ public <T> T removeAttribute(String name) {
}

/**
* Remove all attributes
* Remove all attributes.
*
* @return {@link Resilience4jContext}
*/
Expand All @@ -208,7 +208,7 @@ public Resilience4jContext removeAttributes() {
}

/**
* Get the attributes
* Get the attributes.
*
* @return the read-only attributes
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,15 @@
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import io.github.resilience4j.core.EventConsumer;
import io.github.resilience4j.core.EventProcessor;
import io.github.resilience4j.core.Registry;
import io.github.resilience4j.core.lang.NonNull;
import io.github.resilience4j.core.lang.Nullable;
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.RegistryEventConsumer;
import io.microsphere.logging.Logger;
import io.vavr.CheckedFunction0;
import io.vavr.CheckedRunnable;
Expand All @@ -35,13 +42,28 @@
import static io.microsphere.util.Assert.assertNotNull;

/**
* The abstract template class for Resilience4j
* The abstract template class for Resilience4j supports the common operations:
* <ul>
* <li>One-Time Operation :
* <ul>
* <li>{@link #execute(Supplier, CheckedFunction0)} : execution with result</li>
* <li>{@link #execute(Supplier, CheckedRunnable)} : execution without result</li>
* </ul>
* </li>
* <li>Two-Phase Operation :
* <li>{@link #begin(Supplier)} : the first phase</li>
* <li>{@link #end(Resilience4jContext)} : the second phase</li>
* </li>
* </ul>
*
* @param <E> the type of Resilience4j's entry, e.g., {@link CircuitBreaker}
* @param <C> the type of Resilience4j's entry configuration, e.g., {@link CircuitBreakerConfig}
* @param <R> the type of Resilience4j's entry registry, e.g., {@link CircuitBreakerRegistry}
* @author <a href="mailto:[email protected]">Mercy<a/>
* @see Resilience4jModule
* @see Resilience4jContext
* @see Registry
* @see RegistryEventConsumer
* @since 1.0.0
*/
public abstract class Resilience4jTemplate<E, C, R extends Registry<E, C>> {
Expand All @@ -52,6 +74,8 @@ public abstract class Resilience4jTemplate<E, C, R extends Registry<E, C>> {

protected final Resilience4jModule module;

protected final EventProcessor eventProcessor;

/**
* Local Cache using {@link HashMap} with better performance,
* it's no thread-safe and can be thread-safe if and only if it's initialized by
Expand All @@ -62,6 +86,7 @@ public abstract class Resilience4jTemplate<E, C, R extends Registry<E, C>> {
public Resilience4jTemplate(R registry) {
assertNotNull(registry, "The registry must not be null");
this.registry = registry;
this.eventProcessor = (EventProcessor) registry.getEventPublisher();
this.module = valueOf(registry.getClass());
this.localEntriesCache = new HashMap<>();
}
Expand Down Expand Up @@ -116,24 +141,12 @@ public final Class<C> getConfigClass() {
return (Class<C>) this.module.getConfigClass();
}

/**
* Adds a configuration to the registry
*
* @param configName the configuration name
* @param configuration the added configuration
* @return {@link Resilience4jTemplate}
*/
public Resilience4jTemplate<E, C, R> addConfiguration(String configName, C configuration) {
registry.addConfiguration(configName, configuration);
return this;
}

/**
* Initialize the local entries cache
*
* @param entryNames the names of entries
*/
public Resilience4jTemplate<E, C, R> initLocalEntriesCache(Iterable<String> entryNames) {
public final Resilience4jTemplate<E, C, R> initLocalEntriesCache(Iterable<String> entryNames) {
for (String entryName : entryNames) {
initLocalEntriesCache(entryName);
}
Expand All @@ -145,7 +158,7 @@ public Resilience4jTemplate<E, C, R> initLocalEntriesCache(Iterable<String> entr
*
* @param entryName the name of entry
*/
public Resilience4jTemplate<E, C, R> initLocalEntriesCache(String entryName) {
public final Resilience4jTemplate<E, C, R> initLocalEntriesCache(String entryName) {
E entry = getEntry(entryName);
localEntriesCache.put(entryName, entry);
return this;
Expand Down Expand Up @@ -177,6 +190,7 @@ public final <V> V execute(Supplier<String> entryNameGenerator, CheckedFunction0
V result = null;
try {
result = execute(context, callback);
context.result = result;
} catch (Throwable e) {
context.failure = e;
if (logger.isDebugEnabled()) {
Expand All @@ -203,7 +217,7 @@ public final Resilience4jContext<E> begin(Supplier<String> entryNameGenerator) {
}

/**
* End the execution as the final phase.
* End the execution as the second phase.
*
* @param context {@link Resilience4jContext}
*/
Expand All @@ -212,14 +226,11 @@ public final void end(Resilience4jContext<E> context) {
}

/**
* Destroy :
* <ul>
* <li>clear the local entries cache</li>
* </ul>
* Callback before {@link #execute(Resilience4jContext, CheckedFunction0) execution}
*
* @param context {@link Resilience4jContext}
*/
public void destroy() {
localEntriesCache.clear();
}
protected abstract void beforeExecute(Resilience4jContext<E> context);

/**
* Call the target callback, for instance, the result maybe be wrapped.
Expand All @@ -234,6 +245,14 @@ protected <V> V execute(Resilience4jContext<E> context, CheckedFunction0<V> call
return callback.apply();
}

/**
* Callback after {@link #execute(Resilience4jContext, CheckedFunction0) execution}
*
* @param context {@link Resilience4jContext}
* @return {@link CheckedFunction0#apply()}
*/
protected abstract void afterExecute(Resilience4jContext<E> context);

/**
* Get the Resilience4j's entry by the specified name
*
Expand All @@ -260,6 +279,51 @@ protected final E getEntryFromCache(String name) {
return localEntriesCache.get(name);
}

/**
* Create the Resilience4j's entry.
*
* @param name the name of the Resilience4j's entry
* @return non-null
*/
@NonNull
protected abstract E createEntry(String name);

/**
* Remove the Resilience4j's entry.
*
* @param name the name of the Resilience4j's entry
* @return <code>null</code> if can't be found by <code>name</code>
*/
@Nullable
protected E removeEntry(String name) {
Optional<E> optionalEntry = this.registry.remove(name);
return optionalEntry.orElse(null);
}

/**
* Replace the Resilience4j's entry.
*
* @param name the name of the Resilience4j's entry
* @param newEntry the new Resilience4j's entry
* @return the old Resilience4j's entry if replaced, otherwise <code>null</code>
*/
protected E replaceEntry(String name, E newEntry) {
Optional<E> optionalEntry = this.registry.replace(name, newEntry);
return optionalEntry.orElse(null);
}

/**
* Adds a configuration to the registry
*
* @param configName the configuration name
* @param configuration the added configuration
* @return {@link Resilience4jTemplate}
*/
public Resilience4jTemplate<E, C, R> configuration(String configName, C configuration) {
registry.addConfiguration(configName, configuration);
return this;
}

/**
* Get the {@link C configuration} by the specified name
*
Expand All @@ -273,27 +337,55 @@ protected final C getConfiguration(String configName) {
}

/**
* Create the Resilience4j's entry
* Register the {@link EventConsumer} of {@link EntryAddedEvent}
*
* @param name the name of the Resilience4j's entry
* @return non-null
* @param entryAddedEventEventConsumer the {@link EventConsumer} of {@link EntryAddedEvent}
* @return {@link Resilience4jTemplate}
*/
@NonNull
protected abstract E createEntry(String name);
public final Resilience4jTemplate<E, C, R> onEntryAddedEvent(EventConsumer<EntryAddedEvent<E>> entryAddedEventEventConsumer) {
return consumeEvent(EntryAddedEvent.class, entryAddedEventEventConsumer);
}

/**
* Callback before execution
* Register the {@link EventConsumer} of {@link EntryRemovedEvent}
*
* @param context {@link Resilience4jContext}
* @param entryRemovedEventEventConsumer the {@link EventConsumer} of {@link EntryAddedEvent}
* @return {@link Resilience4jTemplate}
*/
protected abstract void beforeExecute(Resilience4jContext<E> context);
public final Resilience4jTemplate<E, C, R> onEntryRemovedEvent(EventConsumer<EntryRemovedEvent<E>> entryRemovedEventEventConsumer) {
return consumeEvent(EntryRemovedEvent.class, entryRemovedEventEventConsumer);
}

/**
* Callback after execution
* Register the {@link EventConsumer} of {@link EntryReplacedEvent}
*
* @param context {@link Resilience4jContext}
* @return {@link CheckedFunction0#apply()}
* @param entryReplacedEventEventConsumer the {@link EventConsumer} of {@link EntryAddedEvent}
* @return {@link Resilience4jTemplate}
*/
protected abstract void afterExecute(Resilience4jContext<E> context);
public final Resilience4jTemplate<E, C, R> onEntryReplacedEvent(EventConsumer<EntryReplacedEvent<E>> entryReplacedEventEventConsumer) {
return consumeEvent(EntryReplacedEvent.class, entryReplacedEventEventConsumer);
}

/**
* Register the {@link EventConsumer}
*
* @param eventType the type of Resilience4j event
* @param eventConsumer EventConsumer
* @param <T> the type of Resilience4j event
* @return {@link Resilience4jTemplate}
*/
public <T> Resilience4jTemplate<E, C, R> consumeEvent(Class<? super T> eventType, EventConsumer<T> eventConsumer) {
eventProcessor.registerConsumer(eventType.getSimpleName(), eventConsumer);
return this;
}

/**
* Destroy :
* <ul>
* <li>clear the local entries cache</li>
* </ul>
*/
public void destroy() {
localEntriesCache.clear();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,6 @@ public class BulkheadTemplateTest extends AbstractResilience4jTemplateTest<Bulkh

@Test
public void testExecute() {

BulkheadRegistry.EventPublisher<Bulkhead> eventPublisher = this.registry.getEventPublisher();

eventPublisher.onEntryAdded(event -> {
assertEquals(EntryAddedEvent.class, event.getClass());
Object entry = event.getAddedEntry();
assertTrue(BulkheadTemplateTest.super.entryClass.isAssignableFrom(entry.getClass()));
});

Object result = this.template.execute(() -> this.entryName, () -> null);
assertNull(result);
}
Expand Down
Loading

0 comments on commit 49f0322

Please sign in to comment.