TheBrig.java

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
Location : lambda$initialize$2
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem_EdgeCase_InstantlyClosed(com.renomad.minum.web.FullSystemTests)
removed call to com/renomad/minum/security/TheBrig::reviewCurrentInmates → KILLED

74

1.1
Location : initialize
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem_EdgeCase_InstantlyClosed(com.renomad.minum.web.FullSystemTests)
replaced return value with null for com/renomad/minum/security/TheBrig::initialize → KILLED

79

1.1
Location : reviewCurrentInmates
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem_EdgeCase_InstantlyClosed(com.renomad.minum.web.FullSystemTests)
negated conditional → KILLED

83

1.1
Location : reviewCurrentInmates
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem_DisabledSystemRunningMarker(com.renomad.minum.web.FullSystemTests)
removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED

85

1.1
Location : reviewCurrentInmates
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem_EdgeCase_InstantlyClosed(com.renomad.minum.web.FullSystemTests)
removed call to com/renomad/minum/security/TheBrig::processInmateList → KILLED

87

1.1
Location : reviewCurrentInmates
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem_EdgeCase_InstantlyClosed(com.renomad.minum.web.FullSystemTests)
removed call to java/util/concurrent/locks/ReentrantLock::unlock → KILLED

101

1.1
Location : processInmateList
Killed by : com.renomad.minum.security.TheBrigTests
negated conditional → KILLED

2.2
Location : processInmateList
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem(com.renomad.minum.web.FullSystemTests)
changed conditional boundary → KILLED

103

1.1
Location : processInmateList
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem(com.renomad.minum.web.FullSystemTests)
removed call to com/renomad/minum/database/AbstractDb::delete → KILLED

111

1.1
Location : stop
Killed by : com.renomad.minum.security.TheBrigTests
negated conditional → KILLED

114

1.1
Location : stop
Killed by : com.renomad.minum.security.TheBrigTests
removed call to com/renomad/minum/database/AbstractDb::stop → KILLED

122

1.1
Location : sendToJail
Killed by : com.renomad.minum.security.TheBrigTests
removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED

127

1.1
Location : sendToJail
Killed by : com.renomad.minum.security.TheBrigTests
negated conditional → KILLED

129

1.1
Location : sendToJail
Killed by : com.renomad.minum.security.TheBrigTests
Replaced long addition with subtraction → KILLED

135

1.1
Location : sendToJail
Killed by : com.renomad.minum.security.TheBrigTests
Replaced long addition with subtraction → KILLED

140

1.1
Location : sendToJail
Killed by : com.renomad.minum.security.TheBrigTests
removed call to java/util/concurrent/locks/ReentrantLock::unlock → KILLED

142

1.1
Location : sendToJail
Killed by : com.renomad.minum.web.WebTests
replaced boolean return with false for com/renomad/minum/security/TheBrig::sendToJail → KILLED

148

1.1
Location : isInJail
Killed by : com.renomad.minum.web.WebPerformanceTests.test3(com.renomad.minum.web.WebPerformanceTests)
removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED

150

1.1
Location : isInJail
Killed by : com.renomad.minum.security.TheBrigTests
replaced boolean return with false for com/renomad/minum/security/TheBrig::isInJail → KILLED

2.2
Location : isInJail
Killed by : com.renomad.minum.web.WebPerformanceTests.test3(com.renomad.minum.web.WebPerformanceTests)
replaced boolean return with true for com/renomad/minum/security/TheBrig::isInJail → KILLED

3.3
Location : isInJail
Killed by : com.renomad.minum.web.WebPerformanceTests.test3(com.renomad.minum.web.WebPerformanceTests)
negated conditional → KILLED

152

1.1
Location : isInJail
Killed by : com.renomad.minum.web.WebPerformanceTests.test3(com.renomad.minum.web.WebPerformanceTests)
removed call to java/util/concurrent/locks/ReentrantLock::unlock → KILLED

158

1.1
Location : getInmates
Killed by : com.renomad.minum.security.TheBrigTests
removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED

160

1.1
Location : getInmates
Killed by : com.renomad.minum.security.TheBrigTests
replaced return value with Collections.emptyList for com/renomad/minum/security/TheBrig::getInmates → KILLED

162

1.1
Location : getInmates
Killed by : com.renomad.minum.security.TheBrigTests
removed call to java/util/concurrent/locks/ReentrantLock::unlock → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0