All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ldaptive.AbstractConnectionValidator Maven / Gradle / Ivy

There is a newer version: 2.4.1
Show newest version
/* See LICENSE for licensing and NOTICE for copyright. */
package org.ldaptive;

import java.time.Duration;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Base class for connection validator implementations.
 *
 * @author  Middleware Services
 */
public abstract class AbstractConnectionValidator extends AbstractFreezable implements ConnectionValidator
{

  /** Default validation period, value is 30 minutes. */
  public static final Duration DEFAULT_VALIDATE_PERIOD = Duration.ofMinutes(30);

  /** Default per connection validate timeout, value is 5 seconds. */
  public static final Duration DEFAULT_VALIDATE_TIMEOUT = Duration.ofSeconds(5);

  /** Logger for this class. */
  protected final Logger logger = LoggerFactory.getLogger(getClass());

  /** Validation period. */
  private Duration validatePeriod;

  /** Maximum length of time a connection validation should block. */
  private Duration validateTimeout;

  /** Consumer to execute on a successful validation. */
  private Consumer onSuccess;

  /** Consumer to execute on a failed validation. */
  private Consumer onFailure;

  /** Whether the occurrence of a timeout should result in a validation failure. */
  private boolean timeoutIsFailure = true;


  @Override
  public Duration getValidatePeriod()
  {
    return validatePeriod;
  }


  public void setValidatePeriod(final Duration period)
  {
    assertMutable();
    if (period == null || period.isNegative() || period.isZero()) {
      throw new IllegalArgumentException("Period cannot be null, negative or zero");
    }
    validatePeriod = period;
  }

  @Override
  public Duration getValidateTimeout()
  {
    return validateTimeout;
  }


  /**
   * Sets the validate timeout.
   *
   * @param  timeout  to set
   */
  public void setValidateTimeout(final Duration timeout)
  {
    assertMutable();
    if (timeout == null || timeout.isNegative()) {
      throw new IllegalArgumentException("Timeout cannot be null or negative");
    }
    validateTimeout = timeout;
  }


  /**
   * Returns a consumer to handle a connection that has been successfully validated.
   *
   * @return  success consumer
   */
  public Consumer getOnSuccess()
  {
    return onSuccess;
  }


  /**
   * Sets a consumer to handle a connection that has been successfully validated.
   *
   * @param  consumer  to invoke on success
   */
  public void setOnSuccess(final Consumer consumer)
  {
    assertMutable();
    onSuccess = consumer;
  }


  /**
   * Returns a consumer to handle a connection that has failed validation.
   *
   * @return  failure consumer
   */
  public Consumer getOnFailure()
  {
    return onFailure;
  }


  /**
   * Sets a consumer to handle a connection that has failed validation.
   *
   * @param  consumer  to invoke on failure
   */
  public void setOnFailure(final Consumer consumer)
  {
    assertMutable();
    onFailure = consumer;
  }


  /**
   * Returns whether a timeout should be considered a validation failure.
   *
   * @return  whether a timeout should be considered a validation failure
   */
  public boolean getTimeoutIsFailure()
  {
    return timeoutIsFailure;
  }


  /**
   * Sets whether a timeout should be considered a validation failure.
   *
   * @param  failure  whether a timeout should be considered a validation failure
   */
  public void setTimeoutIsFailure(final boolean failure)
  {
    assertMutable();
    timeoutIsFailure = failure;
  }


  @Override
  public Boolean apply(final Connection conn)
  {
    if (conn == null) {
      if (onFailure != null) {
        onFailure.accept(null);
      }
      return false;
    }
    final Boolean result = applyAsync(conn).get();
    if (result && onSuccess != null) {
      onSuccess.accept(conn);
    } else if (!result && onFailure != null) {
      onFailure.accept(conn);
    }
    return result;
  }


  @Override
  public Supplier applyAsync(final Connection conn)
  {
    final CountDownLatch latch = new CountDownLatch(1);
    final AtomicBoolean result = new AtomicBoolean();
    applyAsync(conn, value -> {
      result.compareAndSet(false, value);
      latch.countDown();
    });
    return () -> {
      try {
        if (Duration.ZERO.equals(getValidateTimeout())) {
          // waits indefinitely for the validation response
          latch.await();
        } else {
          if (!latch.await(getValidateTimeout().toMillis(), TimeUnit.MILLISECONDS) && !timeoutIsFailure) {
            logger.debug("Connection validator timeout ignored for {}", conn);
            result.compareAndSet(false, true);
          }
        }
      } catch (Exception e) {
        logger.debug("Validating {} threw unexpected exception", conn, e);
      }
      return result.get();
    };
  }


  @Override
  public String toString()
  {
    return
      getClass().getName() + "@" + hashCode() + "::" +
      "validatePeriod=" + validatePeriod + ", " +
      "validateTimeout=" + validateTimeout + ", " +
      "onSuccess=" + onSuccess + ", " +
      "onFailure=" + onFailure + ", " +
      "timeoutIsFailure=" + timeoutIsFailure;
  }


  /**
   * Base class for validator builders.
   *
   * @param    type of builder
   * @param    type of validator
   */
  protected abstract static class AbstractBuilder
  {

    /** Validator to build. */
    protected final T object;


    /**
     * Creates a new abstract builder.
     *
     * @param  t  validator to build
     */
    protected AbstractBuilder(final T t)
    {
      object = t;
    }


    /**
     * Returns this builder.
     *
     * @return  builder
     */
    protected abstract B self();


    /**
     * Makes this instance immutable.
     *
     * @return  this builder
     */
    public B freeze()
    {
      object.freeze();
      return self();
    }


    /**
     * Sets the validation period.
     *
     * @param  period  to set
     *
     * @return  this builder
     */
    public B period(final Duration period)
    {
      object.setValidatePeriod(period);
      return self();
    }


    /**
     * Sets the validation timeout.
     *
     * @param  timeout  to set
     *
     * @return  this builder
     */
    public B timeout(final Duration timeout)
    {
      object.setValidateTimeout(timeout);
      return self();
    }


    public B onSuccess(final Consumer consumer)
    {
      object.setOnSuccess(consumer);
      return self();
    }


    public B onFailure(final Consumer consumer)
    {
      object.setOnFailure(consumer);
      return self();
    }


    /**
     * Sets whether timeout is a validation failure.
     *
     * @param  failure  whether timeout is a validation failure
     *
     * @return  this builder
     */
    public B timeoutIsFailure(final boolean failure)
    {
      object.setTimeoutIsFailure(failure);
      return self();
    }


    /**
     * Returns the connection validator.
     *
     * @return  connection validator
     */
    public T build()
    {
      return object;
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy