ChainedTimeoutSource.java

package redis.clients.jedis;

/**
 * Chain-of-responsibility link over {@link TimeoutSource}: {@link #get()} consults the overrides
 * first (deepest valid override wins) and falls back to this source's own value when none has an
 * opinion.
 */
class ChainedTimeoutSource implements TimeoutSource {

  private volatile ChainedTimeoutSource override;
  private final TimeoutInfo info;
  private final Object identity;

  ChainedTimeoutSource(TimeoutInfo info) {
    this.info = info;
    this.identity = this.getClass();
  }

  ChainedTimeoutSource(TimeoutInfo info, Object identity) {
    this.info = info;
    this.identity = identity;
  }

  @Override
  public TimeoutInfo get() {
    TimeoutInfo fromOverride = getOverrideInfo();
    return fromOverride != null ? fromOverride : getOwnInfo();
  }

  /** The deepest valid override's timeouts, or {@code null} when no override has an opinion. */
  protected final TimeoutInfo getOverrideInfo() {
    ChainedTimeoutSource o = override;
    return o != null ? o.get() : null;
  }

  protected TimeoutInfo getOwnInfo() {
    return isValid() ? info : null;
  }

  protected boolean isValid() {
    return true;
  }

  /** Appends {@code other} to the end of the chain; the last added override takes precedence. */
  synchronized void addOverride(ChainedTimeoutSource other) {
    if (override == null) {
      override = other;
    } else {
      override.addOverride(other);
    }
  }

  /** Unlinks {@code other} from the chain, preserving any overrides added after it. */
  synchronized void removeOverride(ChainedTimeoutSource other) {
    if (override == other) {
      override = other.override;
    } else if (override != null) {
      override.removeOverride(other);
    }
  }

  ChainedTimeoutSource seekBy(Object identity) {
    if (this.identity == identity) {
      return this;
    }
    ChainedTimeoutSource override = this.override;
    if (override == null) {
      return null;
    }
    return override.seekBy(identity);
  }
}