1 | package com.renomad.minum.security; | |
2 | ||
3 | import com.renomad.minum.state.Constants; | |
4 | import com.renomad.minum.state.Context; | |
5 | import com.renomad.minum.database.Db; | |
6 | import com.renomad.minum.logging.ILogger; | |
7 | import com.renomad.minum.utils.SearchUtils; | |
8 | import com.renomad.minum.utils.ThrowingRunnable; | |
9 | import com.renomad.minum.utils.TimeUtils; | |
10 | ||
11 | import java.util.*; | |
12 | import java.util.concurrent.*; | |
13 | import java.util.concurrent.locks.ReentrantLock; | |
14 | ||
15 | /** | |
16 | * See {@link ITheBrig} | |
17 | */ | |
18 | public final class TheBrig implements ITheBrig { | |
19 | private final ExecutorService es; | |
20 | private final Db<Inmate> inmatesDb; | |
21 | private final ILogger logger; | |
22 | private final Constants constants; | |
23 | private final ReentrantLock lock = new ReentrantLock(); | |
24 | private Thread myThread; | |
25 | ||
26 | /** | |
27 | * How long our inner thread will sleep before waking up to scan | |
28 | * for old keys | |
29 | */ | |
30 | private final int sleepTime; | |
31 | ||
32 | public TheBrig(int sleepTime, Context context) { | |
33 | this.es = context.getExecutorService(); | |
34 | this.constants = context.getConstants(); | |
35 | this.logger = context.getLogger(); | |
36 | this.inmatesDb = context.getDb("the_brig", Inmate.EMPTY); | |
37 | this.sleepTime = sleepTime; | |
38 | } | |
39 | ||
40 | /** | |
41 | * In this class we create a thread that runs throughout the lifetime | |
42 | * of the application, in an infinite loop removing keys from the list | |
43 | * under consideration. | |
44 | */ | |
45 | public TheBrig(Context context) { | |
46 | this(10 * 1000, context); | |
47 | } | |
48 | ||
49 | // Regarding the BusyWait - indeed, we expect that the while loop | |
50 | // below is an infinite loop unless there's an exception thrown, that's what it is. | |
51 | @Override | |
52 | public ITheBrig initialize() { | |
53 | logger.logDebug(() -> "Initializing TheBrig main loop"); | |
54 | ThrowingRunnable innerLoopThread = () -> { | |
55 | Thread.currentThread().setName("TheBrigThread"); | |
56 | myThread = Thread.currentThread(); | |
57 | while (true) { | |
58 | try { | |
59 |
1
1. lambda$initialize$2 : removed call to com/renomad/minum/security/TheBrig::reviewCurrentInmates → TIMED_OUT |
reviewCurrentInmates(); |
60 | } catch (InterruptedException ex) { | |
61 | ||
62 | /* | |
63 | this is what we expect to happen. | |
64 | once this happens, we just continue on. | |
65 | this only gets called when we are trying to shut everything | |
66 | down cleanly | |
67 | */ | |
68 | ||
69 | logger.logDebug(() -> String.format("%s TheBrig is stopped.%n", TimeUtils.getTimestampIsoInstant())); | |
70 | Thread.currentThread().interrupt(); | |
71 | break; | |
72 | } | |
73 | } | |
74 | }; | |
75 | es.submit(ThrowingRunnable.throwingRunnableWrapper(innerLoopThread, logger)); | |
76 |
1
1. initialize : replaced return value with null for com/renomad/minum/security/TheBrig::initialize → KILLED |
return this; |
77 | } | |
78 | ||
79 | private void reviewCurrentInmates() throws InterruptedException { | |
80 | Collection<Inmate> values = inmatesDb.values(); | |
81 |
1
1. reviewCurrentInmates : negated conditional → KILLED |
if (! values.isEmpty()) { |
82 | logger.logTrace(() -> "TheBrig reviewing current inmates. Count: " + values.size()); | |
83 | } | |
84 | var now = System.currentTimeMillis(); | |
85 | ||
86 |
1
1. reviewCurrentInmates : removed call to com/renomad/minum/security/TheBrig::processInmateList → KILLED |
processInmateList(now, values, logger, inmatesDb); |
87 | Thread.sleep(sleepTime); | |
88 | } | |
89 | ||
90 | /** | |
91 | * figure out which clients have paid their dues | |
92 | * @param now the current time, in milliseconds past the epoch | |
93 | * @param inmatesDb the database of all inmates | |
94 | */ | |
95 | static void processInmateList(long now, Collection<Inmate> inmates, ILogger logger, Db<Inmate> inmatesDb) { | |
96 | List<String> keysToRemove = new ArrayList<>(); | |
97 | for (Inmate clientKeyAndDuration : inmates) { | |
98 |
1
1. processInmateList : removed call to com/renomad/minum/security/TheBrig::reviewForParole → KILLED |
reviewForParole(now, keysToRemove, clientKeyAndDuration, logger); |
99 | } | |
100 | for (var k : keysToRemove) { | |
101 | logger.logTrace(() -> "TheBrig: removing " + k + " from jail"); | |
102 |
2
1. lambda$processInmateList$5 : replaced boolean return with false for com/renomad/minum/security/TheBrig::lambda$processInmateList$5 → KILLED 2. lambda$processInmateList$5 : replaced boolean return with true for com/renomad/minum/security/TheBrig::lambda$processInmateList$5 → KILLED |
Inmate inmateToRemove = SearchUtils.findExactlyOne(inmates.stream(), x -> x.getClientId().equals(k)); |
103 |
1
1. processInmateList : removed call to com/renomad/minum/database/Db::delete → KILLED |
inmatesDb.delete(inmateToRemove); |
104 | } | |
105 | } | |
106 | ||
107 | private static void reviewForParole( | |
108 | long now, | |
109 | List<String> keysToRemove, | |
110 | Inmate inmate, | |
111 | ILogger logger) { | |
112 | // if the release time is in the past (that is, the release time is | |
113 | // before / less-than now), add them to the list to be released. | |
114 |
2
1. reviewForParole : changed conditional boundary → TIMED_OUT 2. reviewForParole : negated conditional → KILLED |
if (inmate.getReleaseTime() < now) { |
115 | logger.logTrace(() -> "UnderInvestigation: " + inmate.getClientId() + " has paid its dues as of " + inmate.getReleaseTime() + " and is getting released. Current time: " + now); | |
116 | keysToRemove.add(inmate.getClientId()); | |
117 | } | |
118 | } | |
119 | ||
120 | @Override | |
121 | public void stop() { | |
122 | logger.logDebug(() -> "TheBrig has been told to stop"); | |
123 |
1
1. stop : negated conditional → KILLED |
if (myThread != null) { |
124 | logger.logDebug(() -> "TheBrig: Sending interrupt to thread"); | |
125 | myThread.interrupt(); | |
126 |
1
1. stop : removed call to com/renomad/minum/database/Db::stop → SURVIVED |
this.inmatesDb.stop(); |
127 | } else { | |
128 | throw new MinumSecurityException("TheBrig was told to stop, but it was uninitialized"); | |
129 | } | |
130 | } | |
131 | ||
132 | ||
133 | @Override | |
134 | public boolean sendToJail(String clientIdentifier, long sentenceDuration) { | |
135 |
1
1. sendToJail : negated conditional → KILLED |
if (!constants.isTheBrigEnabled) { |
136 |
1
1. sendToJail : replaced boolean return with true for com/renomad/minum/security/TheBrig::sendToJail → KILLED |
return false; |
137 | } | |
138 |
1
1. sendToJail : removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED |
lock.lock(); // block threads here if multiple are trying to get in - only one gets in at a time |
139 | try { | |
140 | long now = System.currentTimeMillis(); | |
141 | ||
142 |
2
1. lambda$sendToJail$9 : replaced boolean return with true for com/renomad/minum/security/TheBrig::lambda$sendToJail$9 → SURVIVED 2. lambda$sendToJail$9 : replaced boolean return with false for com/renomad/minum/security/TheBrig::lambda$sendToJail$9 → KILLED |
Inmate existingInmate = SearchUtils.findExactlyOne(inmatesDb.values().stream(), x -> x.getClientId().equals(clientIdentifier)); |
143 |
1
1. sendToJail : negated conditional → KILLED |
if (existingInmate == null) { |
144 | // if this is a new inmate, add them | |
145 |
1
1. sendToJail : Replaced long addition with subtraction → KILLED |
long releaseTime = now + sentenceDuration; |
146 | logger.logDebug(() -> "TheBrig: Putting away " + clientIdentifier + " for " + sentenceDuration + " milliseconds. Release time: " + releaseTime + ". Current time: " + now); | |
147 | Inmate newInmate = new Inmate(0L, clientIdentifier, releaseTime); | |
148 | inmatesDb.write(newInmate); | |
149 | } else { | |
150 | // if this is an existing inmate continuing to attack us, just update their duration | |
151 |
1
1. sendToJail : Replaced long addition with subtraction → KILLED |
long releaseTime = existingInmate.getReleaseTime() + sentenceDuration; |
152 | logger.logDebug(() -> "TheBrig: Putting away " + clientIdentifier + " for " + sentenceDuration + " milliseconds. Release time: " + releaseTime + ". Current time: " + now); | |
153 | inmatesDb.write(new Inmate(existingInmate.getIndex(), existingInmate.getClientId(), releaseTime)); | |
154 | } | |
155 | } finally { | |
156 |
1
1. sendToJail : removed call to java/util/concurrent/locks/ReentrantLock::unlock → KILLED |
lock.unlock(); |
157 | } | |
158 |
1
1. sendToJail : replaced boolean return with false for com/renomad/minum/security/TheBrig::sendToJail → KILLED |
return true; |
159 | ||
160 | } | |
161 | ||
162 | @Override | |
163 | public boolean isInJail(String clientIdentifier) { | |
164 |
1
1. isInJail : negated conditional → KILLED |
if (!constants.isTheBrigEnabled) { |
165 |
1
1. isInJail : replaced boolean return with true for com/renomad/minum/security/TheBrig::isInJail → KILLED |
return false; |
166 | } | |
167 |
1
1. isInJail : removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED |
lock.lock(); |
168 | try { | |
169 |
4
1. lambda$isInJail$12 : replaced boolean return with true for com/renomad/minum/security/TheBrig::lambda$isInJail$12 → TIMED_OUT 2. isInJail : replaced boolean return with true for com/renomad/minum/security/TheBrig::isInJail → KILLED 3. isInJail : replaced boolean return with false for com/renomad/minum/security/TheBrig::isInJail → KILLED 4. lambda$isInJail$12 : replaced boolean return with false for com/renomad/minum/security/TheBrig::lambda$isInJail$12 → KILLED |
return inmatesDb.values().stream().anyMatch(x -> x.getClientId().equals(clientIdentifier)); |
170 | } finally { | |
171 |
1
1. isInJail : removed call to java/util/concurrent/locks/ReentrantLock::unlock → KILLED |
lock.unlock(); |
172 | } | |
173 | } | |
174 | ||
175 | @Override | |
176 | public List<Inmate> getInmates() { | |
177 |
1
1. getInmates : removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED |
lock.lock(); |
178 | try { | |
179 |
1
1. getInmates : replaced return value with Collections.emptyList for com/renomad/minum/security/TheBrig::getInmates → KILLED |
return inmatesDb.values().stream().toList(); |
180 | } finally { | |
181 |
1
1. getInmates : removed call to java/util/concurrent/locks/ReentrantLock::unlock → SURVIVED |
lock.unlock(); |
182 | } | |
183 | } | |
184 | ||
185 | } | |
Mutations | ||
59 |
1.1 |
|
76 |
1.1 |
|
81 |
1.1 |
|
86 |
1.1 |
|
98 |
1.1 |
|
102 |
1.1 2.2 |
|
103 |
1.1 |
|
114 |
1.1 2.2 |
|
123 |
1.1 |
|
126 |
1.1 |
|
135 |
1.1 |
|
136 |
1.1 |
|
138 |
1.1 |
|
142 |
1.1 2.2 |
|
143 |
1.1 |
|
145 |
1.1 |
|
151 |
1.1 |
|
156 |
1.1 |
|
158 |
1.1 |
|
164 |
1.1 |
|
165 |
1.1 |
|
167 |
1.1 |
|
169 |
1.1 2.2 3.3 4.4 |
|
171 |
1.1 |
|
177 |
1.1 |
|
179 |
1.1 |
|
181 |
1.1 |