/**
   * The real brains of this scheduler: using the memory capacity reported in each work request,
   * determine how to spread the load of unscheduled jobs efficiently across the requesting agents.
   *
   * <p>The algorithm works by taking the collection of work requests and sorting them from smallest
   * to largest free memory capacity.
   *
   * <p>The scheduler then iterates over each job, handing it to the first agent that has enough
   * capacity to complete it. In this sense, each agent is "greedy", grabbing as many jobs as it can
   * manage.
   *
   * <p>If a job is too big to fit into the remaining capacity of any of the agents, it will remain
   * unscheduled.
   *
   * <p><b>WARNING:</b> If a job is too big for any of the agents, it will sit on the backlog
   * forever!
   *
   * <p>
   */
  private void scheduleJobs() {

    Map<ActorRef, List<Job>> agentJobs = new HashMap<ActorRef, List<Job>>();
    List<Job> scheduledJobs = new ArrayList<>();
    Iterator<Job> iterator = unscheduledJobs.iterator();
    while (iterator.hasNext()) {
      Job job = iterator.next();
      long jobSize = new Long(job.getParams().get(JVMCapacityScheduler.JOB_SIZE));
      sortWorkRequestsByFreeMemory();

      for (JVMCapacityWorkRequest workRequest : workRequests) {
        long freeMemory = workRequest.getFreeMemory();
        ActorRef agent = workRequest.getAgent();
        if (freeMemory >= jobSize) {
          // Add the job to the agent
          List<Job> jobs = agentJobs.get(agent);
          if (jobs == null) {
            jobs = new ArrayList<Job>();
            agentJobs.put(agent, jobs);
          }
          jobs.add(job);
          scheduledJobs.add(job);

          // Decrease agent's available memory
          workRequest.setFreeMemory(freeMemory - jobSize);
          break;
        }
      }
    }

    // Create a schedule
    Schedule schedule = new Schedule();
    for (ActorRef agent : agentJobs.keySet()) {
      schedule.setJobs(agent, agentJobs.get(agent));
    }

    // Dispatch the schedule
    dispatchJobs(schedule);

    // Clear old work requests
    workRequests.clear();

    // Cancel the scheduling
    scheduleJobs.cancel();
  }
 private Job makeJob(int id, int priority, DateTime enqueuedAt) {
   Job job = makeJob(id, priority);
   job.setEnqueuedAt(enqueuedAt);
   return job;
 }
 private Job makeJob(int id, int priority) {
   Job job = new Job(id, null);
   job.setParams(new HashMap<String, String>());
   job.getParams().put("priority", String.valueOf(priority));
   return job;
 }