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