ScanRunContext.java

1
// SPDX-License-Identifier: Apache-2.0
2
// Copyright 2026 Egothor
3
// Copyright 2026 Accenture
4
package org.egothor.methodatlas;
5
6
import java.util.Optional;
7
8
/**
9
 * Thread-local holder for the current {@link ScanRun}, the JUL-friendly
10
 * equivalent of SLF4J's MDC.
11
 *
12
 * <p>
13
 * {@link MethodAtlasApp#run(String[], java.io.PrintWriter)} constructs a
14
 * {@link ScanRun} once at the top of every invocation and calls
15
 * {@link #set(ScanRun)} so that any code executed on the same thread for
16
 * the duration of the run can read the correlation id through
17
 * {@link #current()}. A custom {@code java.util.logging.Formatter} reads
18
 * the context and prepends the run id to every log record (introduced in
19
 * Item 20 of the architecture remediation plan).
20
 * </p>
21
 *
22
 * <h2>Thread safety</h2>
23
 *
24
 * <p>
25
 * Each thread sees its own value. The MethodAtlas CLI runs single-threaded
26
 * scans by default; AI provider calls are sequential. When concurrent
27
 * threads do appear (the {@link java.util.ServiceLoader} per-plugin
28
 * isolation), callers that want the run id on those threads must propagate
29
 * it explicitly.
30
 * </p>
31
 *
32
 * <h2>Cleanup</h2>
33
 *
34
 * <p>
35
 * Always pair {@link #set(ScanRun)} with a {@link #clear()} in a
36
 * {@code finally} block — without it, the thread-local reference outlives
37
 * the CLI invocation in container deployments that pool threads. The
38
 * standard MethodAtlas CLI exits the JVM at end-of-run, so cleanup matters
39
 * mainly when MethodAtlas is invoked programmatically.
40
 * </p>
41
 *
42
 * @since 1.0.0
43
 */
44
public final class ScanRunContext {
45
46
    private static final ThreadLocal<ScanRun> CURRENT = new ThreadLocal<>();
47
48
    private ScanRunContext() {
49
        // Utility class -- instantiation makes no sense.
50
    }
51
52
    /**
53
     * Sets the current scan run for the calling thread.
54
     *
55
     * @param run the run identifier; must not be {@code null}
56
     */
57
    public static void set(ScanRun run) {
58 2 1. set : removed conditional - replaced equality check with false → KILLED
2. set : removed conditional - replaced equality check with true → KILLED
        if (run == null) {
59
            throw new IllegalArgumentException("run must not be null; call clear() to remove the context");
60
        }
61 1 1. set : removed call to java/lang/ThreadLocal::set → KILLED
        CURRENT.set(run);
62
    }
63
64
    /**
65
     * Returns the current scan run for the calling thread, or
66
     * {@link Optional#empty()} when no run is currently set (the standard
67
     * case before {@link MethodAtlasApp#main(String[])} runs or after
68
     * {@link #clear()}).
69
     *
70
     * @return optional carrying the current run
71
     */
72
    public static Optional<ScanRun> current() {
73 1 1. current : replaced return value with Optional.empty for org/egothor/methodatlas/ScanRunContext::current → KILLED
        return Optional.ofNullable(CURRENT.get());
74
    }
75
76
    /**
77
     * Removes the current scan run from the calling thread. Always called in
78
     * a {@code finally} block paired with {@link #set(ScanRun)} so that the
79
     * thread-local reference does not outlive the CLI invocation.
80
     */
81
    public static void clear() {
82 1 1. clear : removed call to java/lang/ThreadLocal::remove → KILLED
        CURRENT.remove();
83
    }
84
}

Mutations

58

1.1
Location : set
Killed by : org.egothor.methodatlas.ScanRunContextTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.ScanRunContextTest]/[method:set_nullArgument_throwsIllegalArgumentException()]
removed conditional - replaced equality check with false → KILLED

2.2
Location : set
Killed by : org.egothor.methodatlas.ScanRunContextTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.ScanRunContextTest]/[method:clear_afterSet_returnsToEmpty()]
removed conditional - replaced equality check with true → KILLED

61

1.1
Location : set
Killed by : org.egothor.methodatlas.ScanRunContextTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.ScanRunContextTest]/[method:set_thenCurrent_returnsTheStoredValue()]
removed call to java/lang/ThreadLocal::set → KILLED

73

1.1
Location : current
Killed by : org.egothor.methodatlas.ScanRunContextTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.ScanRunContextTest]/[method:set_thenCurrent_returnsTheStoredValue()]
replaced return value with Optional.empty for org/egothor/methodatlas/ScanRunContext::current → KILLED

82

1.1
Location : clear
Killed by : org.egothor.methodatlas.ScanRunContextTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.ScanRunContextTest]/[method:clear_afterSet_returnsToEmpty()]
removed call to java/lang/ThreadLocal::remove → KILLED

Active mutators

Tests examined


Report generated by PIT 1.22.1