/*
 * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0
 * (the "License"). You may not use this work except in compliance with the License, which is
 * available at www.apache.org/licenses/LICENSE-2.0
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied, as more fully set forth in the License.
 *
 * See the NOTICE file distributed with this work for information regarding copyright ownership.
 */

package alluxio.master.lineage.recompute;

import alluxio.exception.AccessControlException;
import alluxio.exception.FileDoesNotExistException;
import alluxio.exception.InvalidPathException;
import alluxio.exception.UnexpectedAlluxioException;
import alluxio.heartbeat.HeartbeatExecutor;
import alluxio.master.file.FileSystemMaster;
import alluxio.master.lineage.meta.Lineage;
import alluxio.master.lineage.meta.LineageStateUtils;
import alluxio.util.ThreadFactoryUtils;

import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.Futures;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import javax.annotation.concurrent.ThreadSafe;

/**
 * A periodical executor that detects lost files and launches recompute jobs.
 */
@ThreadSafe
public final class RecomputeExecutor implements HeartbeatExecutor {
  private static final Logger LOG = LoggerFactory.getLogger(RecomputeExecutor.class);

  private static final int DEFAULT_RECOMPUTE_LAUNCHER_POOL_SIZE = 10;
  private final RecomputePlanner mPlanner;
  private final FileSystemMaster mFileSystemMaster;
  /** The thread pool to launch recompute jobs. */
  private final ExecutorService mRecomputeLauncherService =
      Executors.newFixedThreadPool(DEFAULT_RECOMPUTE_LAUNCHER_POOL_SIZE,
          ThreadFactoryUtils.build("recompute-launcher-%d", true));

  /**
   * Creates a new instance of {@link RecomputeExecutor}.
   *
   * @param planner recompute planner
   * @param fileSystemMaster the file system master
   */
  public RecomputeExecutor(RecomputePlanner planner, FileSystemMaster fileSystemMaster) {
    mPlanner = Preconditions.checkNotNull(planner);
    mFileSystemMaster = Preconditions.checkNotNull(fileSystemMaster);
  }

  @Override
  public void heartbeat() {
    heartbeatWithFuture();
  }

  @Override
  public void close() {
    mRecomputeLauncherService.shutdown();
  }

  /**
   * A version of {@code heartbeat} which returns a {@link Future} representing completion of the
   * recompute plan. This is especially useful for tests.
   *
   * @return the {@code Future} representing completion of the recompute plan
   */
  Future<?> heartbeatWithFuture() {
    RecomputePlan plan = mPlanner.plan();
    if (plan != null && !plan.isEmpty()) {
      return mRecomputeLauncherService.submit(new RecomputeLauncher(plan));
    }
    return Futures.<Void>immediateFuture(null);
  }

  /**
   * Thread to launch the recompute jobs in a given plan.
   */
  @ThreadSafe
  final class RecomputeLauncher implements Runnable {
    private final RecomputePlan mPlan;

    /**
     * Creates a new instance of {@link RecomputeLauncher}.
     *
     * @param plan the recompute plan
     */
    RecomputeLauncher(RecomputePlan plan) {
      mPlan = Preconditions.checkNotNull(plan);
    }

    @Override
    public void run() {
      for (Lineage lineage : mPlan.getLineageToRecompute()) {
        // empty all the lost files
        try {
          for (Long fileId : LineageStateUtils.getLostFiles(lineage,
              mFileSystemMaster.getFileSystemMasterView())) {
            try {
              mFileSystemMaster.resetFile(fileId);
            } catch (UnexpectedAlluxioException e) {
              LOG.error("the lost file {} can not be freed", fileId, e);
            } catch (FileDoesNotExistException e) {
              LOG.error("the lost file {} does not exist", fileId, e);
            } catch (InvalidPathException e) {
              LOG.error("the lost file {} is invalid", fileId, e);
            } catch (AccessControlException e) {
              LOG.error("the lost file {} cannot be accessed", fileId, e);
            }
          }
        } catch (FileDoesNotExistException e) {
          LOG.error("an output file of lineage {} does not exist", lineage.getId(), e);
        }

        boolean success = lineage.getJob().run();
        if (!success) {
          LOG.error("Failed to recompute job {}", lineage.getJob());
        }
      }
    }
  }
}

@@ -2276,16 +2276,27 @@
 (planner
+, %22planner%22
 );%0A    m
@@ -2357,16 +2357,36 @@
 emMaster
+, %22fileSystemMaster%22
 );%0A  %7D%0A%0A

add variable name for Preconditions.checkNotNull of RecomputeExecutor.java