JobService.java
package edu.ucsb.cs156.frontiers.services.jobs;
import edu.ucsb.cs156.frontiers.entities.Job;
import edu.ucsb.cs156.frontiers.repositories.JobsRepository;
import edu.ucsb.cs156.frontiers.services.CurrentUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Slf4j
public class JobService {
@Autowired private JobsRepository jobsRepository;
@Autowired private CurrentUserService currentUserService;
@Autowired private JobContextFactory contextFactory;
/*
* This is a self-referential bean to allow for async method calls within the same class.
*/
@Lazy @Autowired private JobService self;
public Job runAsJob(JobContextConsumer jobFunction) {
String jobName = jobFunction.getClass().getName().replace("edu.ucsb.cs156.frontiers.jobs.", "");
Job job =
Job.builder()
.createdBy(currentUserService.getUser())
.status("running")
.jobName(jobName)
.build();
log.info("Starting job: {}, jobName={}", job.getId(), job.getJobName());
jobsRepository.save(job);
self.runJobAsync(job, jobFunction);
return job;
}
/**
* Runs a job asynchronously.
*
* <p>This method is annotated with @Transactional because outside of the Spring context, you
* cannot delete entities that are unmanaged by Hibernate. Adding @Transactional keeps the
* database session open and allows Hibernate to maintain it's knowledge of the object graph (i.e.
* the entities)
*
* <p>To learn more, read about Hibernate and the concept of a Spring Context.
*
* <p>Note that @Transactional means that if there is an unhandled exception, either every
* database transactions succeeds, or all of them are rolled back.
*
* @param job
* @param jobFunction
*/
@Async
@Transactional
public void runJobAsync(Job job, JobContextConsumer jobFunction) {
JobContext context = contextFactory.createContext(job);
try {
jobFunction.accept(context);
} catch (Exception e) {
job.setStatus("error");
context.log(e.getMessage());
return;
}
job.setStatus("complete");
jobsRepository.save(job);
}
public String getJobLogs(Long jobId) {
Job job =
jobsRepository
.findById(jobId)
.orElseThrow(() -> new IllegalArgumentException("Job not found"));
String log = job.getLog();
return log != null ? log : "";
}
}