/**
   * Accept offers as advised by the launch coordinator.
   *
   * <p>Acceptance is routed through the RM to update the persistent state before forwarding the
   * message to Mesos.
   */
  private void acceptOffers(AcceptOffers msg) {

    try {
      List<TaskMonitor.TaskGoalStateUpdated> toMonitor = new ArrayList<>(msg.operations().size());

      // transition the persistent state of some tasks to Launched
      for (Protos.Offer.Operation op : msg.operations()) {
        if (op.getType() != Protos.Offer.Operation.Type.LAUNCH) {
          continue;
        }
        for (Protos.TaskInfo info : op.getLaunch().getTaskInfosList()) {
          MesosWorkerStore.Worker worker = workersInNew.remove(extractResourceID(info.getTaskId()));
          assert (worker != null);

          worker = worker.launchWorker(info.getSlaveId(), msg.hostname());
          workerStore.putWorker(worker);
          workersInLaunch.put(extractResourceID(worker.taskID()), worker);

          LOG.info(
              "Launching Mesos task {} on host {}.",
              worker.taskID().getValue(),
              worker.hostname().get());

          toMonitor.add(new TaskMonitor.TaskGoalStateUpdated(extractGoalState(worker)));
        }
      }

      // tell the task router about the new plans
      for (TaskMonitor.TaskGoalStateUpdated update : toMonitor) {
        taskRouter.tell(update, self());
      }

      // send the acceptance message to Mesos
      schedulerDriver.acceptOffers(msg.offerIds(), msg.operations(), msg.filters());
    } catch (Exception ex) {
      fatalError("unable to accept offers", ex);
    }
  }
  @Override
  public void resourceOffers(SchedulerDriver driver, List<Protos.Offer> offers) {
    long masterCpu = Configuration.getInt(PropertyKey.INTEGRATION_MASTER_RESOURCE_CPU);
    long masterMem =
        Configuration.getBytes(PropertyKey.INTEGRATION_MASTER_RESOURCE_MEM) / Constants.MB;
    long workerCpu = Configuration.getInt(PropertyKey.INTEGRATION_WORKER_RESOURCE_CPU);
    long workerMem =
        Configuration.getBytes(PropertyKey.INTEGRATION_WORKER_RESOURCE_MEM) / Constants.MB;

    LOG.info(
        "Master launched {}, master count {}, "
            + "requested master cpu {} mem {} MB and required master hostname {}",
        mMasterLaunched,
        mMasterCount,
        masterCpu,
        masterMem,
        mRequiredMasterHostname);

    for (Protos.Offer offer : offers) {
      Protos.Offer.Operation.Launch.Builder launch = Protos.Offer.Operation.Launch.newBuilder();
      double offerCpu = 0;
      double offerMem = 0;
      for (Protos.Resource resource : offer.getResourcesList()) {
        if (resource.getName().equals(Constants.MESOS_RESOURCE_CPUS)) {
          offerCpu += resource.getScalar().getValue();
        } else if (resource.getName().equals(Constants.MESOS_RESOURCE_MEM)) {
          offerMem += resource.getScalar().getValue();
        } else {
          // Other resources are currently ignored.
        }
      }

      LOG.info(
          "Received offer {} on host {} with cpus {} and mem {} MB and hasMasterPorts {}",
          offer.getId().getValue(),
          offer.getHostname(),
          offerCpu,
          offerMem,
          OfferUtils.hasAvailableMasterPorts(offer));

      Protos.ExecutorInfo.Builder executorBuilder = Protos.ExecutorInfo.newBuilder();
      List<Protos.Resource> resources;
      if (!mMasterLaunched
          && offerCpu >= masterCpu
          && offerMem >= masterMem
          && mMasterCount
              < Configuration.getInt(PropertyKey.INTEGRATION_MESOS_ALLUXIO_MASTER_NODE_COUNT)
          && OfferUtils.hasAvailableMasterPorts(offer)
          && (mRequiredMasterHostname == null
              || mRequiredMasterHostname.equals(offer.getHostname()))) {
        LOG.debug("Creating Alluxio Master executor");
        executorBuilder
            .setName("Alluxio Master Executor")
            .setSource("master")
            .setExecutorId(Protos.ExecutorID.newBuilder().setValue("master"))
            .addAllResources(getExecutorResources())
            .setCommand(
                Protos.CommandInfo.newBuilder()
                    .setValue(createStartAlluxioCommand("alluxio-master-mesos.sh"))
                    .addAllUris(getExecutorDependencyURIList())
                    .setEnvironment(
                        Protos.Environment.newBuilder()
                            .addVariables(
                                Protos.Environment.Variable.newBuilder()
                                    .setName("ALLUXIO_UNDERFS_ADDRESS")
                                    .setValue(Configuration.get(PropertyKey.UNDERFS_ADDRESS))
                                    .build())
                            .build()));
        // pre-build resource list here, then use it to build Protos.Task later.
        resources = getMasterRequiredResources(masterCpu, masterMem);
        mMasterHostname = offer.getHostname();
        mTaskName = Configuration.get(PropertyKey.INTEGRATION_MESOS_ALLUXIO_MASTER_NAME);
        mMasterCount++;
        mMasterTaskId = mLaunchedTasks;
      } else if (mMasterLaunched
          && !mWorkers.contains(offer.getHostname())
          && offerCpu >= workerCpu
          && offerMem >= workerMem
          && OfferUtils.hasAvailableWorkerPorts(offer)) {
        LOG.debug("Creating Alluxio Worker executor");
        final String memSize = FormatUtils.getSizeFromBytes((long) workerMem * Constants.MB);
        executorBuilder
            .setName("Alluxio Worker Executor")
            .setSource("worker")
            .setExecutorId(Protos.ExecutorID.newBuilder().setValue("worker"))
            .addAllResources(getExecutorResources())
            .setCommand(
                Protos.CommandInfo.newBuilder()
                    .setValue(createStartAlluxioCommand("alluxio-worker-mesos.sh"))
                    .addAllUris(getExecutorDependencyURIList())
                    .setEnvironment(
                        Protos.Environment.newBuilder()
                            .addVariables(
                                Protos.Environment.Variable.newBuilder()
                                    .setName("ALLUXIO_MASTER_HOSTNAME")
                                    .setValue(mMasterHostname)
                                    .build())
                            .addVariables(
                                Protos.Environment.Variable.newBuilder()
                                    .setName("ALLUXIO_WORKER_MEMORY_SIZE")
                                    .setValue(memSize)
                                    .build())
                            .addVariables(
                                Protos.Environment.Variable.newBuilder()
                                    .setName("ALLUXIO_UNDERFS_ADDRESS")
                                    .setValue(Configuration.get(PropertyKey.UNDERFS_ADDRESS))
                                    .build())
                            .build()));
        // pre-build resource list here, then use it to build Protos.Task later.
        resources = getWorkerRequiredResources(workerCpu, workerMem);
        mWorkers.add(offer.getHostname());
        mTaskName = Configuration.get(PropertyKey.INTEGRATION_MESOS_ALLUXIO_WORKER_NAME);
      } else {
        // The resource offer cannot be used to start either master or a worker.
        LOG.info("Declining offer {}", offer.getId().getValue());
        driver.declineOffer(offer.getId());
        continue;
      }

      Protos.TaskID taskId =
          Protos.TaskID.newBuilder().setValue(String.valueOf(mLaunchedTasks)).build();

      LOG.info("Launching task {} using offer {}", taskId.getValue(), offer.getId().getValue());

      Protos.TaskInfo task =
          Protos.TaskInfo.newBuilder()
              .setName(mTaskName)
              .setTaskId(taskId)
              .setSlaveId(offer.getSlaveId())
              .addAllResources(resources)
              .setExecutor(executorBuilder)
              .build();

      launch.addTaskInfos(Protos.TaskInfo.newBuilder(task));
      mLaunchedTasks++;

      // NOTE: We use the new API `acceptOffers` here to launch tasks.
      // The 'launchTasks' API will be deprecated.
      List<Protos.OfferID> offerIds = new ArrayList<Protos.OfferID>();
      offerIds.add(offer.getId());
      List<Protos.Offer.Operation> operations = new ArrayList<Protos.Offer.Operation>();
      Protos.Offer.Operation operation =
          Protos.Offer.Operation.newBuilder()
              .setType(Protos.Offer.Operation.Type.LAUNCH)
              .setLaunch(launch)
              .build();
      operations.add(operation);
      Protos.Filters filters = Protos.Filters.newBuilder().setRefuseSeconds(1).build();
      driver.acceptOffers(offerIds, operations, filters);
    }
  }