JobContext.java

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
Location : log
Killed by : edu.ucsb.cs156.jobs.services.JobContextTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.jobs.services.JobContextTests]/[method:null_jobLogRepository_is_tolerated_as_a_test_seam()]
negated conditional → KILLED

56

1.1
Location : log
Killed by : edu.ucsb.cs156.jobs.services.JobContextTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.jobs.services.JobContextTests]/[method:null_jobLogRepository_is_tolerated_as_a_test_seam()]
negated conditional → KILLED

57

1.1
Location : log
Killed by : edu.ucsb.cs156.jobs.services.JobContextTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.jobs.services.JobContextTests]/[method:null_jobLogRepository_is_tolerated_as_a_test_seam()]
removed call to edu/ucsb/cs156/jobs/entities/Job::setLog → KILLED

61

1.1
Location : log
Killed by : edu.ucsb.cs156.jobs.services.JobContextTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.jobs.services.JobContextTests]/[method:each_log_call_is_its_own_insert()]
negated conditional → KILLED

62

1.1
Location : log
Killed by : edu.ucsb.cs156.jobs.services.JobContextTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.jobs.services.JobContextTests]/[method:with_a_log_transaction_template_the_insert_runs_inside_it()]
removed call to org/springframework/transaction/support/TransactionTemplate::executeWithoutResult → KILLED

75

1.1
Location : getJob
Killed by : edu.ucsb.cs156.jobs.services.JobContextTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.jobs.services.JobContextTests]/[method:getJob_exposes_the_underlying_job()]
replaced return value with null for edu/ucsb/cs156/jobs/services/JobContext::getJob → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0