/** Stops the processing and prints the final time. */
 @Override
 public void close() {
   logger.info("Finished processing.");
   this.timer.stop();
   this.lastSeconds = (int) (timer.getTotalWallTime() / 1000000000);
   printStatus();
 }
  /**
   * Counts one entity. Every once in a while, the current time is checked so as to print an
   * intermediate report roughly every ten seconds.
   */
  private void countEntity() {
    if (!this.timer.isRunning()) {
      startTimer();
    }

    this.entityCount++;
    if (this.entityCount % 100 == 0) {
      timer.stop();
      int seconds = (int) (timer.getTotalWallTime() / 1000000000);
      if (seconds >= this.lastSeconds + this.reportInterval) {
        this.lastSeconds = seconds;
        printStatus();
        if (this.timeout > 0 && seconds > this.timeout) {
          logger.info("Timeout. Aborting processing.");
          throw new TimeoutException();
        }
      }
      timer.start();
    }
  }
/**
 * Simple {@link EntityDocumentProcessor} for basic counting and time keeping. It will print
 * statistics on elapsed time and processed entities once in a while. The class also supports a
 * timeout mechanism: if a timeout time (in seconds) is given, then a {@link
 * EntityTimerProcessor.TimeoutException} (unchecked) will be thrown soon after this many seconds
 * have passed. This can be used to abort processing in a relatively clean way by catching this
 * exception at a higher level.
 *
 * @author Markus Kroetzsch
 */
public class EntityTimerProcessor implements EntityDocumentDumpProcessor {

  static final Logger logger = LoggerFactory.getLogger(EntityTimerProcessor.class);

  final Timer timer = Timer.getNamedTimer("EntityTimerProcessor");
  final int timeout;
  int entityCount = 0;
  int lastSeconds = 0;

  /**
   * Number of seconds after which a progress report is printed. If a timeout is configured, it will
   * only be checked at a report.
   */
  int reportInterval = 10;

  /**
   * Constructor.
   *
   * @param timeout the timeout in seconds or 0 if no timeout should be used
   */
  public EntityTimerProcessor(int timeout) {
    this.timeout = timeout;
  }

  /**
   * Sets the interval after which the timer should report progress. By default, this is ten
   * seconds. When using a timeout, the timeout condition will only be checked at this interval,
   * too, so using a very large value would lead to increasing imprecision with the timeout. The
   * timer does not use a separate thread, and reports will only be generated after an entity was
   * fully processed. Thus, very long processing times would also affect the accuracy of the
   * interval.
   *
   * @param seconds time after which progress should be reported.
   */
  public void setReportInterval(int seconds) {
    if (seconds <= 0) {
      throw new IllegalArgumentException(
          "The report interval must be a non-zero, positive number of seconds.");
    }
    this.reportInterval = seconds;
  }

  @Override
  public void processItemDocument(ItemDocument itemDocument) {
    countEntity();
  }

  @Override
  public void processPropertyDocument(PropertyDocument propertyDocument) {
    countEntity();
  }

  @Override
  public void open() {
    // Nothing to do. We only start the timer when the first entity is
    // really processed.
  }

  /** Stops the processing and prints the final time. */
  @Override
  public void close() {
    logger.info("Finished processing.");
    this.timer.stop();
    this.lastSeconds = (int) (timer.getTotalWallTime() / 1000000000);
    printStatus();
  }

  /**
   * Counts one entity. Every once in a while, the current time is checked so as to print an
   * intermediate report roughly every ten seconds.
   */
  private void countEntity() {
    if (!this.timer.isRunning()) {
      startTimer();
    }

    this.entityCount++;
    if (this.entityCount % 100 == 0) {
      timer.stop();
      int seconds = (int) (timer.getTotalWallTime() / 1000000000);
      if (seconds >= this.lastSeconds + this.reportInterval) {
        this.lastSeconds = seconds;
        printStatus();
        if (this.timeout > 0 && seconds > this.timeout) {
          logger.info("Timeout. Aborting processing.");
          throw new TimeoutException();
        }
      }
      timer.start();
    }
  }

  /** Prints the current status, time and entity count. */
  private void printStatus() {
    logger.info(
        "Processed "
            + this.entityCount
            + " entities in "
            + this.lastSeconds
            + " sec"
            + (this.lastSeconds > 0
                ? " (" + (this.entityCount / this.lastSeconds) + " per second)"
                : ""));
  }

  private void startTimer() {
    logger.info("Starting processing.");
    this.timer.start();
  }

  public class TimeoutException extends RuntimeException {
    private static final long serialVersionUID = -1083533602730765194L;
  }
}