| 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 → TIMED_OUT |
return this; |
| 77 | } | |
| 78 | ||
| 79 | private void reviewCurrentInmates() throws InterruptedException { | |
| 80 | Collection<Inmate> values = inmatesDb.values(); | |
| 81 |
1
1. reviewCurrentInmates : negated conditional → TIMED_OUT |
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 → TIMED_OUT |
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 true for com/renomad/minum/security/TheBrig::lambda$processInmateList$5 → TIMED_OUT 2. lambda$processInmateList$5 : replaced boolean return with false 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 → KILLED |
this.inmatesDb.stop(); |
| 127 | } else { | |
| 128 | throw new MinumSecurityException("TheBrig was told to stop, but it was uninitialized"); | |
| 129 | } | |
| 130 | } | |
| 131 | ||
| 132 | @Override | |
| 133 | public boolean sendToJail(String clientIdentifier, long sentenceDuration) { | |
| 134 |
1
1. sendToJail : negated conditional → KILLED |
if (!constants.isTheBrigEnabled) { |
| 135 |
1
1. sendToJail : replaced boolean return with true for com/renomad/minum/security/TheBrig::sendToJail → KILLED |
return false; |
| 136 | } | |
| 137 |
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 |
| 138 | try { | |
| 139 | long now = System.currentTimeMillis(); | |
| 140 | ||
| 141 |
2
1. lambda$sendToJail$9 : replaced boolean return with true for com/renomad/minum/security/TheBrig::lambda$sendToJail$9 → KILLED 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)); |
| 142 |
1
1. sendToJail : negated conditional → KILLED |
if (existingInmate == null) { |
| 143 | // if this is a new inmate, add them | |
| 144 |
1
1. sendToJail : Replaced long addition with subtraction → KILLED |
long releaseTime = now + sentenceDuration; |
| 145 | logger.logDebug(() -> "TheBrig: Putting away " + clientIdentifier + " for " + sentenceDuration + " milliseconds. Release time: " + releaseTime + ". Current time: " + now); | |
| 146 | Inmate newInmate = new Inmate(0L, clientIdentifier, releaseTime); | |
| 147 | inmatesDb.write(newInmate); | |
| 148 | } else { | |
| 149 | // if this is an existing inmate continuing to attack us, just update their duration | |
| 150 |
1
1. sendToJail : Replaced long addition with subtraction → KILLED |
long releaseTime = existingInmate.getReleaseTime() + sentenceDuration; |
| 151 | logger.logDebug(() -> "TheBrig: Putting away " + clientIdentifier + " for " + sentenceDuration + " milliseconds. Release time: " + releaseTime + ". Current time: " + now); | |
| 152 | inmatesDb.write(new Inmate(existingInmate.getIndex(), existingInmate.getClientId(), releaseTime)); | |
| 153 | } | |
| 154 | } finally { | |
| 155 |
1
1. sendToJail : removed call to java/util/concurrent/locks/ReentrantLock::unlock → KILLED |
lock.unlock(); |
| 156 | } | |
| 157 |
1
1. sendToJail : replaced boolean return with false for com/renomad/minum/security/TheBrig::sendToJail → KILLED |
return true; |
| 158 | ||
| 159 | } | |
| 160 | ||
| 161 | @Override | |
| 162 | public boolean isInJail(String clientIdentifier) { | |
| 163 |
1
1. isInJail : negated conditional → KILLED |
if (!constants.isTheBrigEnabled) { |
| 164 |
1
1. isInJail : replaced boolean return with true for com/renomad/minum/security/TheBrig::isInJail → KILLED |
return false; |
| 165 | } | |
| 166 |
1
1. isInJail : removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED |
lock.lock(); |
| 167 | try { | |
| 168 |
4
1. isInJail : replaced boolean return with true for com/renomad/minum/security/TheBrig::isInJail → TIMED_OUT 2. isInJail : replaced boolean return with false for com/renomad/minum/security/TheBrig::isInJail → KILLED 3. lambda$isInJail$12 : replaced boolean return with false for com/renomad/minum/security/TheBrig::lambda$isInJail$12 → KILLED 4. lambda$isInJail$12 : replaced boolean return with true for com/renomad/minum/security/TheBrig::lambda$isInJail$12 → KILLED |
return inmatesDb.values().stream().anyMatch(x -> x.getClientId().equals(clientIdentifier)); |
| 169 | } finally { | |
| 170 |
1
1. isInJail : removed call to java/util/concurrent/locks/ReentrantLock::unlock → KILLED |
lock.unlock(); |
| 171 | } | |
| 172 | } | |
| 173 | ||
| 174 | @Override | |
| 175 | public List<Inmate> getInmates() { | |
| 176 |
1
1. getInmates : removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED |
lock.lock(); |
| 177 | try { | |
| 178 |
1
1. getInmates : replaced return value with Collections.emptyList for com/renomad/minum/security/TheBrig::getInmates → KILLED |
return inmatesDb.values().stream().toList(); |
| 179 | } finally { | |
| 180 |
1
1. getInmates : removed call to java/util/concurrent/locks/ReentrantLock::unlock → SURVIVED |
lock.unlock(); |
| 181 | } | |
| 182 | } | |
| 183 | ||
| 184 | } | |
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 |
|
| 134 |
1.1 |
|
| 135 |
1.1 |
|
| 137 |
1.1 |
|
| 141 |
1.1 2.2 |
|
| 142 |
1.1 |
|
| 144 |
1.1 |
|
| 150 |
1.1 |
|
| 155 |
1.1 |
|
| 157 |
1.1 |
|
| 163 |
1.1 |
|
| 164 |
1.1 |
|
| 166 |
1.1 |
|
| 168 |
1.1 2.2 3.3 4.4 |
|
| 170 |
1.1 |
|
| 176 |
1.1 |
|
| 178 |
1.1 |
|
| 180 |
1.1 |