// Path: src/main/java/com/indeed/proctor/webapp/extensions/JobInfoStore.java
// public interface JobInfoStore {
//     boolean shouldUpdateJobInfo(final BackgroundJob job);
// 
//     void updateJobInfo(final UUID uuid, final BackgroundJob.JobInfo jobInfo);
// 
//     BackgroundJob.JobInfo getJobInfo(final UUID uuid);
// }
// 
// Path: src/main/java/com/indeed/proctor/webapp/util/ThreadPoolExecutorVarExports.java
// public class ThreadPoolExecutorVarExports {
//     public ThreadPoolExecutor executor;
// 
//     public ThreadPoolExecutorVarExports(final ThreadPoolExecutor executor) {
//         this.executor = executor;
//     }
// 
//     @Export(name = "total-task-count")
//     public long getTaskCount() {
//         return executor.getTaskCount();
//     }
// 
//     @Export(name = "completed-task-count")
//     public long getCompletedTaskCount() {
//         return executor.getCompletedTaskCount();
//     }
// 
//     @Export(name = "active-threads")
//     public long getActiveCount() {
//         return executor.getActiveCount();
//     }
// 
//     @Export(name = "pool-size")
//     public long getPoolSize() {
//         return executor.getPoolSize();
//     }
// 
//     @Export(name = "max-pool-size")
//     public long getMaximumPoolSize() {
//         return executor.getMaximumPoolSize();
//     }
// 
//     @Export(name = "queue-remaining-capacity")
//     public int getQueueRemainingCapacity() {
//         return executor.getQueue().remainingCapacity();
//     }
// 
//     @Export(name = "core-pool-size")
//     public long getCorePoolSize() {
//         return executor.getCorePoolSize();
//     }
// 
//     @Export(name = "keep-alive-time")
//     public String getKeepAliveTime() {
//         return executor.getKeepAliveTime(TimeUnit.SECONDS) + " seconds";
//     }
// 
//     @Export(name = "queue-size")
//     public int getQueueSize() {
//         return executor.getQueue().size();
//     }
// 
//     @Export(name = "shutdown")
//     public boolean isShutdown() {
//         return executor.isShutdown();
//     }
// }
// 
// Path: src/main/java/com/indeed/proctor/webapp/util/threads/LogOnUncaughtExceptionHandler.java
// public class LogOnUncaughtExceptionHandler implements UncaughtExceptionHandler {
//     private Logger logger = Logger.getLogger(LogOnUncaughtExceptionHandler.class);
// 
//     public LogOnUncaughtExceptionHandler(){}
// 
// 
//     public LogOnUncaughtExceptionHandler(final Logger logger) {
//         this.logger = logger;
//     }
// 
//     public void setLogger(final Logger logger) {
//         this.logger = logger;
//     }
// 
//     @Override
//     public void uncaughtException(final Thread t, final Throwable e) {
//         logger.error("Uncaught throwable in thread " + t.getName() + "/" + t.getId(), e);
//     }
// }


import com.google.common.collect.Lists;
import com.google.common.collect.MapMaker;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.indeed.proctor.webapp.extensions.JobInfoStore;
import com.indeed.proctor.webapp.util.ThreadPoolExecutorVarExports;
import com.indeed.proctor.webapp.util.threads.LogOnUncaughtExceptionHandler;
import com.indeed.util.varexport.VarExporter;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import javax.annotation.Nullable;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
package com.indeed.proctor.webapp.jobs;



@EnableScheduling
public class BackgroundJobManager {
    private static final Logger LOGGER = Logger.getLogger(BackgroundJobManager.class);

    private final List<BackgroundJob> backgroundJobs = Lists.newLinkedList();
    private final ExecutorService service;
    private final Map<UUID, BackgroundJob> history = new MapMaker()
            .softValues()
            .makeMap();
    private final AtomicLong lastId = new AtomicLong(0);

// Path: src/main/java/com/indeed/proctor/webapp/extensions/JobInfoStore.java
// public interface JobInfoStore {
//     boolean shouldUpdateJobInfo(final BackgroundJob job);
// 
//     void updateJobInfo(final UUID uuid, final BackgroundJob.JobInfo jobInfo);
// 
//     BackgroundJob.JobInfo getJobInfo(final UUID uuid);
// }
// 
// Path: src/main/java/com/indeed/proctor/webapp/util/ThreadPoolExecutorVarExports.java
// public class ThreadPoolExecutorVarExports {
//     public ThreadPoolExecutor executor;
// 
//     public ThreadPoolExecutorVarExports(final ThreadPoolExecutor executor) {
//         this.executor = executor;
//     }
// 
//     @Export(name = "total-task-count")
//     public long getTaskCount() {
//         return executor.getTaskCount();
//     }
// 
//     @Export(name = "completed-task-count")
//     public long getCompletedTaskCount() {
//         return executor.getCompletedTaskCount();
//     }
// 
//     @Export(name = "active-threads")
//     public long getActiveCount() {
//         return executor.getActiveCount();
//     }
// 
//     @Export(name = "pool-size")
//     public long getPoolSize() {
//         return executor.getPoolSize();
//     }
// 
//     @Export(name = "max-pool-size")
//     public long getMaximumPoolSize() {
//         return executor.getMaximumPoolSize();
//     }
// 
//     @Export(name = "queue-remaining-capacity")
//     public int getQueueRemainingCapacity() {
//         return executor.getQueue().remainingCapacity();
//     }
// 
//     @Export(name = "core-pool-size")
//     public long getCorePoolSize() {
//         return executor.getCorePoolSize();
//     }
// 
//     @Export(name = "keep-alive-time")
//     public String getKeepAliveTime() {
//         return executor.getKeepAliveTime(TimeUnit.SECONDS) + " seconds";
//     }
// 
//     @Export(name = "queue-size")
//     public int getQueueSize() {
//         return executor.getQueue().size();
//     }
// 
//     @Export(name = "shutdown")
//     public boolean isShutdown() {
//         return executor.isShutdown();
//     }
// }
// 
// Path: src/main/java/com/indeed/proctor/webapp/util/threads/LogOnUncaughtExceptionHandler.java
// public class LogOnUncaughtExceptionHandler implements UncaughtExceptionHandler {
//     private Logger logger = Logger.getLogger(LogOnUncaughtExceptionHandler.class);
// 
//     public LogOnUncaughtExceptionHandler(){}
// 
// 
//     public LogOnUncaughtExceptionHandler(final Logger logger) {
//         this.logger = logger;
//     }
// 
//     public void setLogger(final Logger logger) {
//         this.logger = logger;
//     }
// 
//     @Override
//     public void uncaughtException(final Thread t, final Throwable e) {
//         logger.error("Uncaught throwable in thread " + t.getName() + "/" + t.getId(), e);
//     }
// }


// Path: src/main/java/com/indeed/proctor/webapp/jobs/BackgroundJobManager.java
import com.google.common.collect.Lists;
import com.google.common.collect.MapMaker;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.indeed.proctor.webapp.extensions.JobInfoStore;
import com.indeed.proctor.webapp.util.ThreadPoolExecutorVarExports;
import com.indeed.proctor.webapp.util.threads.LogOnUncaughtExceptionHandler;
import com.indeed.util.varexport.VarExporter;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import javax.annotation.Nullable;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

package com.indeed.proctor.webapp.jobs;



@EnableScheduling
public class BackgroundJobManager {
    private static final Logger LOGGER = Logger.getLogger(BackgroundJobManager.class);

    private final List<BackgroundJob> backgroundJobs = Lists.newLinkedList();
    private final ExecutorService service;
    private final Map<UUID, BackgroundJob> history = new MapMaker()
            .softValues()
            .makeMap();
    private final AtomicLong lastId = new AtomicLong(0);

    private JobInfoStore jobInfoStore;