| 1 | package edu.ucsb.cs156.jobs.services; | |
| 2 | ||
| 3 | import edu.ucsb.cs156.jobs.entities.Job; | |
| 4 | import edu.ucsb.cs156.jobs.entities.JobLog; | |
| 5 | import edu.ucsb.cs156.jobs.repositories.JobLogRepository; | |
| 6 | import edu.ucsb.cs156.jobs.repositories.JobsRepository; | |
| 7 | import lombok.extern.slf4j.Slf4j; | |
| 8 | import org.springframework.transaction.support.TransactionTemplate; | |
| 9 | ||
| 10 | /** | |
| 11 | * Handed to a running {@link JobContextConsumer}; each {@link #log} call appends one line to the | |
| 12 | * job's log, so admins can watch progress live. | |
| 13 | * | |
| 14 | * <p>Since v0.2.0, each line is its own row in {@code job_logs} (see {@link JobLog}) rather than a | |
| 15 | * read-modify-write of one growing TEXT column on {@code jobs} — an O(1) insert instead of an O(N) | |
| 16 | * rewrite of everything logged so far. The job body runs inside one all-or-nothing transaction (see | |
| 17 | * {@link JobService#runJobAsync}), so log writes must NOT join it: they would be invisible to the | |
| 18 | * admin UI until the whole job commits. When a {@code logTransactionTemplate} is provided | |
| 19 | * (configured REQUIRES_NEW by {@link JobContextFactory}), each line commits in its own transaction | |
| 20 | * immediately. | |
| 21 | * | |
| 22 | * <p>A null {@code jobLogRepository} is tolerated as a test seam: the log accumulates on {@code | |
| 23 | * job}'s (Java-only, {@code @Transient}) {@code log} field instead of being persisted anywhere. The | |
| 24 | * apps' job tests conventionally run jobs against {@code new JobContext(null, job)} and assert on | |
| 25 | * {@code job.getLog()}; that legacy two-arg constructor is preserved unchanged for exactly this | |
| 26 | * reason. | |
| 27 | */ | |
| 28 | @Slf4j | |
| 29 | public class JobContext { | |
| 30 | private final JobLogRepository jobLogRepository; | |
| 31 | private final Job job; | |
| 32 | private final TransactionTemplate logTransactionTemplate; | |
| 33 | ||
| 34 | /** | |
| 35 | * @deprecated kept only so existing test code written against v0.1.x ({@code new JobContext(null, | |
| 36 | * job)}) keeps compiling; the {@code jobsRepository} parameter is ignored. Use {@link | |
| 37 | * #JobContext(JobLogRepository, Job, TransactionTemplate)}. | |
| 38 | */ | |
| 39 | @Deprecated | |
| 40 | public JobContext(JobsRepository jobsRepository, Job job) { | |
| 41 | this((JobLogRepository) null, job, null); | |
| 42 | } | |
| 43 | ||
| 44 | public JobContext( | |
| 45 | JobLogRepository jobLogRepository, Job job, TransactionTemplate logTransactionTemplate) { | |
| 46 | this.jobLogRepository = jobLogRepository; | |
| 47 | this.job = job; | |
| 48 | this.logTransactionTemplate = logTransactionTemplate; | |
| 49 | } | |
| 50 | ||
| 51 | public void log(String message) { | |
| 52 | log.info("Job {}: {}", job.getId(), message); | |
| 53 |
1
1. log : negated conditional → KILLED |
if (jobLogRepository == null) { |
| 54 | // Test seam: no persistence available, so accumulate in-memory on the | |
| 55 | // (Java-only) Job.log field, exactly as v0.1.x did for every caller. | |
| 56 |
1
1. log : negated conditional → KILLED |
String previousLog = job.getLog() == null ? "" : (job.getLog() + "\n"); |
| 57 |
1
1. log : removed call to edu/ucsb/cs156/jobs/entities/Job::setLog → KILLED |
job.setLog(previousLog + message); |
| 58 | return; | |
| 59 | } | |
| 60 | JobLog entry = JobLog.builder().jobId(job.getId()).message(message).build(); | |
| 61 |
1
1. log : negated conditional → KILLED |
if (logTransactionTemplate != null) { |
| 62 |
1
1. log : removed call to org/springframework/transaction/support/TransactionTemplate::executeWithoutResult → KILLED |
logTransactionTemplate.executeWithoutResult(status -> jobLogRepository.save(entry)); |
| 63 | } else { | |
| 64 | jobLogRepository.save(entry); | |
| 65 | } | |
| 66 | } | |
| 67 | ||
| 68 | /** | |
| 69 | * Exposes the underlying job, mainly so job bodies and their tests can inspect state (e.g. {@code | |
| 70 | * ctx.getJob().getLog()}) without threading a separate reference through. Note: for a real | |
| 71 | * (non-test-seam) run, {@code job.getLog()} is not kept up to date by {@link #log} any more (see | |
| 72 | * class javadoc) — it reflects whatever the caller last set it to, typically nothing. | |
| 73 | */ | |
| 74 | public Job getJob() { | |
| 75 |
1
1. getJob : replaced return value with null for edu/ucsb/cs156/jobs/services/JobContext::getJob → KILLED |
return job; |
| 76 | } | |
| 77 | } | |
Mutations | ||
| 53 |
1.1 |
|
| 56 |
1.1 |
|
| 57 |
1.1 |
|
| 61 |
1.1 |
|
| 62 |
1.1 |
|
| 75 |
1.1 |