TriggerCatalog.java
package com.redis.test.fi;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
/**
* Preloaded catalog of one effect family: every constant of an {@link EffectType} enum resolved to
* its {@link Effect} in one discovery pass. The catalog is static per FI instance.
*/
public final class TriggerCatalog {
private final Map<EffectType, Effect> effects;
private TriggerCatalog(Map<EffectType, Effect> effects) {
this.effects = effects;
}
public static <E extends Enum<E> & EffectType> TriggerCatalog resolve(FaultInjectorClient client,
Class<E> family) {
Map<EffectType, Effect> effects = new LinkedHashMap<>();
for (E type : family.getEnumConstants()) {
effects.put(type, client.discover(type));
}
return new TriggerCatalog(effects);
}
/**
* This catalog without the triggers matching {@code dropped}, for what the FI advertises but the
* server cannot run (the FI's catalog is built from the cluster size alone). Effects stay even
* when all their triggers are dropped, so {@link #effect} still answers for them.
*/
public TriggerCatalog without(Predicate<Trigger> dropped) {
Map<EffectType, Effect> kept = new LinkedHashMap<>();
effects.forEach((type, effect) -> kept.put(type, effect.without(dropped)));
return new TriggerCatalog(kept);
}
/** The discovered effect; fails loudly for a type outside this catalog's family. */
public Effect effect(EffectType type) {
Effect effect = effects.get(type);
if (effect == null) {
throw new IllegalArgumentException(type + " is not in this catalog: " + effects.keySet());
}
return effect;
}
/** All triggers of all effects, in discovery order. */
public List<Trigger> allTriggers() {
List<Trigger> all = new ArrayList<>();
for (Effect effect : effects.values()) {
all.addAll(effect.triggers());
}
return all;
}
/**
* Whether the FI offers the combination on this server: effect, trigger and requirement config.
*/
public boolean offers(ScenarioKey key) {
Effect effect = effects.get(key.effect());
return effect != null && effect.triggers().stream()
.anyMatch(t -> t.name().equals(key.trigger()) && t.offers(key.config()));
}
/** The runnable scenario for the key; fails loudly when the FI does not offer it. */
public Scenario scenario(ScenarioKey key) {
return effect(key.effect()).trigger(key.trigger()).scenario(key.config());
}
}