ArchivePolicy.java

package gov.usgs.earthquake.indexer;

import gov.usgs.earthquake.distribution.ConfigurationException;
import gov.usgs.earthquake.util.ISO8601;
import gov.usgs.util.Config;
import gov.usgs.util.DefaultConfigurable;

import java.math.BigDecimal;
import java.util.Date;
import java.util.logging.Logger;

/**
 * A policy for the Indexer to clean up Events in its ProductIndex.
 *
 * The policy is created by configuration parameters and generates a
 * ProductIndexQuery. Any product/event matching the product index query is
 * archived. Generally, archiving means the data for that product/event is
 * removed from the index as well as the storage. Upon archiving, an
 * EVENT_ARCHIVED type of IndexerEvent is sent to interested listeners.
 *
 * All archive policies run in their own thread (one thread separate from
 * indexing for all archive policies, not one each) and execute at configured
 * intervals.
 *
 * @author emartinez
 *
 */
public class ArchivePolicy extends DefaultConfigurable {

  private static final Logger LOGGER = Logger.getLogger(ArchivePolicy.class.getName());

  // --------------------------------------------------------------------
  // Names of configurable parameters
  // --------------------------------------------------------------------

  /** @deprecated */
  @Deprecated
  public static final String ARCHIVE_MIN_AGE_PROPERTY = "minAge";
  /** @deprecated */
  @Deprecated
  public static final String ARCHIVE_MAX_AGE_PROPERTY = "maxAge";

  /** Property for archive minimum event age */
  public static final String ARCHIVE_MIN_EVENT_AGE_PROPERTY = "minEventAge";
  /** Property for archive maximum event age */
  public static final String ARCHIVE_MAX_EVENT_AGE_PROPERTY = "maxEventAge";

  /** Property for archive minimum event time */
  public static final String ARCHIVE_MIN_EVENT_TIME_PROPERTY = "minEventTime";
  /** Property for archive maximum event time */
  public static final String ARCHIVE_MAX_EVENT_TIME_PROPERTY = "maxEventTime";

  /** Property for archive minimum mag */
  public static final String ARCHIVE_MIN_MAG_PROPERTY = "minMag";
  /** Property for archive maximum mag */
  public static final String ARCHIVE_MAX_MAG_PROPERTY = "maxMag";

  /** Property for archive minimum latitude */
  public static final String ARCHIVE_MIN_LAT_PROPERTY = "minLat";
  /** Property for archive maximum latitude */
  public static final String ARCHIVE_MAX_LAT_PROPERTY = "maxLat";

  /** Property for archive minimum longitude */
  public static final String ARCHIVE_MIN_LNG_PROPERTY = "minLng";
  /** Property for archive maximum longitude */
  public static final String ARCHIVE_MAX_LNG_PROPERTY = "maxLng";

  /** Property for archive minimum depth */
  public static final String ARCHIVE_MIN_DEPTH_PROPERTY = "minDepth";
  /** Property for archive maximum depth */
  public static final String ARCHIVE_MAX_DEPTH_PROPERTY = "maxDepth";

  /** Property for archive event source */
  public static final String ARCHIVE_EVENT_SOURCE_PROPERTY = "eventSource";

  // --------------------------------------------------------------------
  // Configured parameters.
  // --------------------------------------------------------------------

  /** @deprecated */
  @Deprecated
  protected Long minAge = null;
  /** @deprecated */
  @Deprecated
  protected Long maxAge = null;

  /** Configured parameter var for minEventAge */
  protected Long minEventAge = null;
  /** Configured parameter var for maxEventAge */
  protected Long maxEventAge = null;

  /** Configured parameter var for minEventTime */
  protected Long minEventTime = null;
  /** Configured parameter var for maxEventTime */
  protected Long maxEventTime = null;

  /** Configured parameter var for minMag */
  protected BigDecimal minMag = null;
  /** Configured parameter var for maxMag */
  protected BigDecimal maxMag = null;

  /** Configured parameter var for minLat */
  protected BigDecimal minLat = null;
  /** Configured parameter var for maxLat */
  protected BigDecimal maxLat = null;

  /** Configured parameter var for minLng */
  protected BigDecimal minLng = null;
  /** Configured parameter var for maxLng */
  protected BigDecimal maxLng = null;

  /** Configured parameter var for minDepth */
  protected BigDecimal minDepth = null;
  /** Configured parameter var for maxDepth */
  protected BigDecimal maxDepth = null;

  /** Configured parameter var for eventSource */
  protected String eventSource = null;

  /** Default Constructor */
  public ArchivePolicy() {
    // Default constructor
  }

  @Override
  public void configure(Config config) throws Exception {
    minEventAge = parseLong(config, ARCHIVE_MIN_EVENT_AGE_PROPERTY);
    maxEventAge = parseLong(config, ARCHIVE_MAX_EVENT_AGE_PROPERTY);

    minEventTime = parseDateOrLong(config, ARCHIVE_MIN_EVENT_TIME_PROPERTY);
    maxEventTime = parseDateOrLong(config, ARCHIVE_MAX_EVENT_TIME_PROPERTY);

    minAge = parseLong(config, ARCHIVE_MIN_AGE_PROPERTY);
    if (minAge != null) {
      LOGGER.config("Use of minAge property is deprecated.");
    }
    maxAge = parseLong(config, ARCHIVE_MAX_AGE_PROPERTY);
    if (maxAge != null) {
      LOGGER.config("Use of maxAge property is deprecated.");
    }

    if (minEventAge != null && maxEventTime != null) {
      LOGGER.config("Both minEventAge and maxEventTime were specified. "
          + "Ignoring minEventAge. Only maxEventTime will be used.");
    }
    if (maxEventAge != null && minEventTime != null) {
      LOGGER.config("Both maxEventAge and minEventTime were specified. "
          + "Ignoring maxEventAge. Only minEventTime will be used.");
    }

    if ((minAge != null || maxAge != null)
        && (minEventAge != null || maxEventAge != null || minEventTime != null || maxEventTime != null)) {

      ConfigurationException ce = new ConfigurationException("Configuration mismatch. Can not specify both "
          + "minAge/maxAge (legacy) properties as well as " + "minEventAge/maxEventAge or minEventTime/maxEventTime.");

      ce.fillInStackTrace();
      throw ce;
    }

    if ((minEventAge != null && maxEventAge != null) && (minEventAge > maxEventAge)) {

      ConfigurationException ce = new ConfigurationException(
          "Configuration mismatch. minEventAge " + "greater than maxEventAge.");
      ce.fillInStackTrace();
      throw ce;
    }

    if ((minEventTime != null && maxEventTime != null) && (minEventTime > maxEventTime)) {

      ConfigurationException ce = new ConfigurationException(
          "Configuration mismatch. minEventTime " + "greater than maxEventTime.");
      ce.fillInStackTrace();
      throw ce;
    }

    minMag = parseBigDecimal(config, ARCHIVE_MIN_MAG_PROPERTY);
    maxMag = parseBigDecimal(config, ARCHIVE_MAX_MAG_PROPERTY);
    minLat = parseBigDecimal(config, ARCHIVE_MIN_LAT_PROPERTY);
    maxLat = parseBigDecimal(config, ARCHIVE_MAX_LAT_PROPERTY);
    minLng = parseBigDecimal(config, ARCHIVE_MIN_LNG_PROPERTY);
    maxLng = parseBigDecimal(config, ARCHIVE_MAX_LNG_PROPERTY);
    minDepth = parseBigDecimal(config, ARCHIVE_MIN_DEPTH_PROPERTY);
    maxDepth = parseBigDecimal(config, ARCHIVE_MAX_DEPTH_PROPERTY);
    eventSource = config.getProperty(ARCHIVE_EVENT_SOURCE_PROPERTY);
  }

  @Override
  public void shutdown() throws Exception {
    // Nothing to do
  }

  @Override
  public void startup() throws Exception {
    // Nothing to do
  }

  /** @return a ProductIndexQuery */
  public ProductIndexQuery getIndexQuery() {
    ProductIndexQuery productIndexQuery = new ProductIndexQuery();
    Date now = new Date();

    if (minAge != null) {
      // min age corresponds to minimum event time
      productIndexQuery.setMinEventTime(new Date(now.getTime() - minAge.longValue()));
    }
    if (maxAge != null) {
      // max age corresponds to maximum event time
      productIndexQuery.setMaxEventTime(new Date(now.getTime() - maxAge.longValue()));
    }

    /*-
     Epoch                                                            Now
       |------------------- maxAge <---------------------------------- |
       |                       |                                       |
       |                       |                minAge <---------------|
       |                       |                   |                   |
       |-----------------------|-------------------|-------------------|
       |                       |                   |
       |------------------> minTime                |
       |                                           |
       |--------------------------------------> maxTime
    
     Simple Example (not to scale)
    
       0 (Epoch)                                              (Now) 100,000
       |------------------- maxAge <--- (10,000) --------------------- |
       |                       |                                       |
       |                       |                minAge <--- (1,000) ---|
       |                       |                   |                   |
       |-------------------- 90,000 ************ 99,000 ---------------|
       |                       |                   |
       |----- (90,000) ---> minTime                |
       |                                           |
       |------------------------- (99,000) ---> maxTime
    
       Events occurring in the *** time span will match the query and be
       archived.
     */

    if (maxEventAge != null) {
      productIndexQuery.setMinEventTime(new Date(now.getTime() - maxEventAge.longValue()));
    }
    if (minEventAge != null) {
      productIndexQuery.setMaxEventTime(new Date(now.getTime() - minEventAge.longValue()));
    }

    if (minEventTime != null) {
      productIndexQuery.setMinEventTime(new Date(minEventTime.longValue()));
    }
    if (maxEventTime != null) {
      productIndexQuery.setMaxEventTime(new Date(maxEventTime.longValue()));
    }

    productIndexQuery.setMinEventMagnitude(minMag);
    productIndexQuery.setMaxEventMagnitude(maxMag);
    productIndexQuery.setMinEventLatitude(minLat);
    productIndexQuery.setMaxEventLatitude(maxLat);
    productIndexQuery.setMinEventLongitude(minLng);
    productIndexQuery.setMaxEventLongitude(maxLng);
    productIndexQuery.setMinEventDepth(minDepth);
    productIndexQuery.setMaxEventDepth(maxDepth);
    productIndexQuery.setEventSource(eventSource);

    // this archive policy is only for events, only remove events based on
    // their preferred properties
    productIndexQuery.setEventSearchType(ProductIndexQuery.SEARCH_EVENT_PREFERRED);

    productIndexQuery.setResultType(ProductIndexQuery.RESULT_TYPE_ALL);

    return productIndexQuery;
  }

  /** @return boolean if the policy is valid */
  public boolean isValidPolicy() {
    // Not valid if using both old and new configuration methods
    boolean valid = !((minAge != null || maxAge != null) && (minEventAge != null || maxEventAge != null));

    return valid
        && (minAge != null || maxAge != null || minEventAge != null || maxEventAge != null || minEventTime != null
            || maxEventTime != null || minMag != null || maxMag != null || minLat != null || maxLat != null
            || minLng != null || maxLng != null || minDepth != null || maxDepth != null || eventSource != null);
  }

  /**
   * Gets the property 'name' from config and returns a BigDecimal of it
   *
   * @param config Config file
   * @param name   name of property from config
   * @return BigDecimal of property
   */
  protected BigDecimal parseBigDecimal(Config config, String name) {
    BigDecimal property = null;
    try {
      String buffer = config.getProperty(name);
      if (buffer != null) {
        property = new BigDecimal(buffer);
      }
    } catch (NumberFormatException npx) {
      property = null;
    }
    return property;
  }

  /**
   * Gets the property 'name' from config and returns a Date/Long of it
   *
   * @param config Config file
   * @param name   name of property from config
   * @return Date/Long of property
   */
  protected Long parseDateOrLong(Config config, String name) {
    Long property = null;
    try {
      String buffer = config.getProperty(name);
      if (buffer != null) {
        if (buffer.indexOf("T") != -1) {
          // try parsing as date
          Date date = ISO8601.parse(buffer);
          if (date != null) {
            property = date.getTime();
          }
        } else {
          property = Long.valueOf(buffer);
        }
      }
    } catch (NumberFormatException npx) {
      property = null;
    }
    return property;
  }

  /**
   * Gets the property 'name' from config and returns a Long of it
   *
   * @param config Config file
   * @param name   name of property from config
   * @return Long of property
   */
  protected Long parseLong(Config config, String name) {
    Long property = null;
    try {
      String buffer = config.getProperty(name);
      if (buffer != null) {
        property = Long.valueOf(buffer);
      }
    } catch (NumberFormatException npx) {
      property = null;
    }
    return property;
  }

  /**
   * @deprecated
   * @return minAge
   */
  @Deprecated
  public Long getMinAge() {
    return minAge;
  }

  /**
   * @deprecated
   * @param minAge to set
   */
  @Deprecated
  public void setMinAge(Long minAge) {
    this.minAge = minAge;
  }

  /**
   * @deprecated
   * @return maxAge
   */
  @Deprecated
  public Long getMaxAge() {
    return maxAge;
  }

  /**
   * @deprecated
   * @param maxAge to set
   */
  @Deprecated
  public void setMaxAge(Long maxAge) {
    this.maxAge = maxAge;
  }

  /** @return minEventAge */
  public Long getMinEventAge() {
    return minEventAge;
  }

  /** @param minEventAge to set */
  public void setMinEventAge(Long minEventAge) {
    this.minEventAge = minEventAge;
  }

  /** @return maxEventAge */
  public Long getMaxEventAge() {
    return maxEventAge;
  }

  /** @param maxEventAge to set */
  public void setMaxEventAge(Long maxEventAge) {
    this.maxEventAge = maxEventAge;
  }

  /** @return minEventTime */
  public Long getMinEventTime() {
    return minEventTime;
  }

  /** @param minEventTime to set */
  public void setMinEventTime(Long minEventTime) {
    this.minEventTime = minEventTime;
  }

  /** @return maxEventTime */
  public Long getMaxEventTime() {
    return maxEventTime;
  }

  /** @param maxEventTime to set */
  public void setMaxEventTime(Long maxEventTime) {
    this.maxEventTime = maxEventTime;
  }

  /** @return minMag */
  public BigDecimal getMinMag() {
    return minMag;
  }

  /** @param minMag to set */
  public void setMinMag(BigDecimal minMag) {
    this.minMag = minMag;
  }

  /** @return maxMag */
  public BigDecimal getMaxMag() {
    return maxMag;
  }

  /** @param maxMag to set */
  public void setMaxMag(BigDecimal maxMag) {
    this.maxMag = maxMag;
  }

  /** @return minLat */
  public BigDecimal getMinLat() {
    return minLat;
  }

  /** @param minLat to set */
  public void setMinLat(BigDecimal minLat) {
    this.minLat = minLat;
  }

  /** @return maxLat */
  public BigDecimal getMaxLat() {
    return maxLat;
  }

  /** @param maxLat to set */
  public void setMaxLat(BigDecimal maxLat) {
    this.maxLat = maxLat;
  }

  /** @return minLng */
  public BigDecimal getMinLng() {
    return minLng;
  }

  /** @param minLng to set */
  public void setMinLng(BigDecimal minLng) {
    this.minLng = minLng;
  }

  /** @return maxLng */
  public BigDecimal getMaxLng() {
    return maxLng;
  }

  /** @param maxLng to set */
  public void setMaxLng(BigDecimal maxLng) {
    this.maxLng = maxLng;
  }

  /** @return minDepth */
  public BigDecimal getMinDepth() {
    return minDepth;
  }

  /** @param minDepth to set */
  public void setMinDepth(BigDecimal minDepth) {
    this.minDepth = minDepth;
  }

  /** @return maxDepth */
  public BigDecimal getMaxDepth() {
    return maxDepth;
  }

  /** @param maxDepth to set */
  public void setMaxDepth(BigDecimal maxDepth) {
    this.maxDepth = maxDepth;
  }

  /** @return eventSource */
  public String getEventSource() {
    return eventSource;
  }

  /** @param eventSource to set */
  public void setEventSource(String eventSource) {
    this.eventSource = eventSource;
  }
}