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

Mutations

58

1.1
Location : lambda$initialize$2
Killed by : none
removed call to com/renomad/minum/security/TheBrig::reviewCurrentInmates → TIMED_OUT

75

1.1
Location : initialize
Killed by : none
replaced return value with null for com/renomad/minum/security/TheBrig::initialize → TIMED_OUT

80

1.1
Location : reviewCurrentInmates
Killed by : none
negated conditional → TIMED_OUT

84

1.1
Location : reviewCurrentInmates
Killed by : none
removed call to java/util/concurrent/locks/ReentrantLock::lock → TIMED_OUT

86

1.1
Location : reviewCurrentInmates
Killed by : none
removed call to com/renomad/minum/security/TheBrig::processInmateList → TIMED_OUT

88

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

102

1.1
Location : processInmateList
Killed by : none
negated conditional → TIMED_OUT

2.2
Location : processInmateList
Killed by : none
changed conditional boundary → TIMED_OUT

104

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

112

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

115

1.1
Location : stop
Killed by : none
removed call to com/renomad/minum/database/AbstractDb::stop → TIMED_OUT

123

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

128

1.1
Location : sendToJail
Killed by : com.renomad.minum.web.WebTests
negated conditional → KILLED

130

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

136

1.1
Location : sendToJail
Killed by : none
Replaced long addition with subtraction → TIMED_OUT

141

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

143

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

149

1.1
Location : isInJail
Killed by : none
removed call to java/util/concurrent/locks/ReentrantLock::lock → TIMED_OUT

151

1.1
Location : isInJail
Killed by : none
replaced boolean return with false for com/renomad/minum/security/TheBrig::isInJail → TIMED_OUT

2.2
Location : isInJail
Killed by : none
replaced boolean return with true for com/renomad/minum/security/TheBrig::isInJail → TIMED_OUT

3.3
Location : isInJail
Killed by : none
negated conditional → TIMED_OUT

153

1.1
Location : isInJail
Killed by : none
removed call to java/util/concurrent/locks/ReentrantLock::unlock → TIMED_OUT

159

1.1
Location : getInmates
Killed by : none
removed call to java/util/concurrent/locks/ReentrantLock::lock → TIMED_OUT

161

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

163

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