FullSystem.java

1
package com.renomad.minum.web;
2
3
import com.renomad.minum.queue.ActionQueueKiller;
4
import com.renomad.minum.state.Constants;
5
import com.renomad.minum.logging.ILogger;
6
import com.renomad.minum.logging.Logger;
7
import com.renomad.minum.security.ITheBrig;
8
import com.renomad.minum.security.TheBrig;
9
import com.renomad.minum.state.Context;
10
import com.renomad.minum.utils.*;
11
12
import java.nio.file.Files;
13
import java.nio.file.Path;
14
import java.time.ZoneId;
15
import java.time.ZonedDateTime;
16
import java.util.concurrent.CancellationException;
17
import java.util.concurrent.ExecutionException;
18
import java.util.concurrent.ExecutorService;
19
import java.util.concurrent.Executors;
20
21
/**
22
 * This class is responsible for instantiating necessary classes
23
 * for a valid system, in the proper order.
24
 * @see #initialize()
25
 * @see #start()
26
 */
27
public final class FullSystem {
28
29
    final ILogger logger;
30
    private final Constants constants;
31
    private final FileUtils fileUtils;
32
    private IServer server;
33
    private WebFramework webFramework;
34
    private IServer sslServer;
35
    Thread shutdownHook;
36
    private ITheBrig theBrig;
37
    final ExecutorService es;
38
    private WebEngine webEngine;
39
40
    /**
41
     * This flag gives us some control if we need
42
     * to call {@link #shutdown()} manually, so close()
43
     * doesn't get run again when the shutdownHook
44
     * tries calling it.  This is primarily an issue just during
45
     * testing.
46
     */
47
    private boolean hasShutdown;
48
49
    private final Context context;
50
51
    /**
52
     * This constructor requires a {@link Context} object,
53
     * but it is easier and recommended to use {@link #initialize()}
54
     * instead.
55
     */
56
    public FullSystem(Context context) {
57
        this.logger = context.getLogger();
58
        this.constants = context.getConstants();
59
        this.fileUtils = new FileUtils(logger, constants);
60
        this.es = context.getExecutorService();
61
        this.context = context;
62 1 1. <init> : removed call to com/renomad/minum/state/Context::setFullSystem → KILLED
        context.setFullSystem(this);
63
    }
64
65
    /**
66
     * Builds a context object that is appropriate as a
67
     * parameter to constructing a {@link FullSystem}
68
     */
69
    public static Context buildContext() {
70
        var constants = new Constants();
71
        var executorService = Executors.newVirtualThreadPerTaskExecutor();
72
        var logger = new Logger(constants, executorService, "primary logger");
73
74 1 1. buildContext : replaced return value with null for com/renomad/minum/web/FullSystem::buildContext → KILLED
        return new Context(executorService, constants, logger);
75
    }
76
77
    /**
78
     * This is the typical entry point for system instantiation.  It will build
79
     * a {@link Context} object for you, and then properly instantiates the {@link FullSystem}.
80
     * <p>
81
     *     <em>Here is an example of a simple Main file using this method:</em>
82
     * </p>
83
     * <pre>{@code
84
     *   package org.example;
85
     *
86
     *   import com.renomad.minum.web.FullSystem;
87
     *   import com.renomad.minum.web.Response;
88
     *
89
     *   import static com.renomad.minum.web.RequestLine.Method.GET;
90
     *
91
     *   public class Main {
92
     *
93
     *       public static void main(String[] args) {
94
     *           FullSystem fs = FullSystem.initialize();
95
     *           fs.getWebFramework().registerPath(GET, "", request -> Response.htmlOk("<p>Hi there world!</p>"));
96
     *           fs.block();
97
     *       }
98
     *   }
99
     * }</pre>
100
     */
101
    public static FullSystem initialize() {
102
        var context = buildContext();
103
        var fullSystem = new FullSystem(context);
104 1 1. initialize : replaced return value with null for com/renomad/minum/web/FullSystem::initialize → KILLED
        return fullSystem.start();
105
    }
106
107
    /**
108
     * This method runs the necessary methods for starting the Minum
109
     * web server.  It is unlikely you will want to use this, unless you
110
     * require it for more control in testing.
111
     * @see #initialize()
112
     */
113
    public FullSystem start() {
114
        // create a file in our current working directory to indicate we are running
115 2 1. start : removed call to com/renomad/minum/web/FullSystem::createSystemRunningMarker → KILLED
2. start : negated conditional → KILLED
        if (constants.enableSystemRunningMarker) createSystemRunningMarker();
116
117
        // set up an action to take place if the user shuts us down
118 1 1. start : removed call to com/renomad/minum/web/FullSystem::addShutdownHook → KILLED
        addShutdownHook();
119
120
        // instantiate our security code
121 1 1. start : negated conditional → KILLED
        if (constants.isTheBrigEnabled) {
122
            theBrig = new TheBrig(context).initialize();
123
        } else {
124
            theBrig = null;
125
        }
126
        
127
        // the web framework handles the HTTP communications
128
        webFramework = new WebFramework(context);
129
130
        // kick off the servers - low level internet handlers
131
        webEngine = new WebEngine(context, webFramework);
132
        server = webEngine.startServer();
133
        sslServer = webEngine.startSslServer();
134
135
        // document how long it took to start up the system
136
        var now = ZonedDateTime.now(ZoneId.of("UTC"));
137
        var nowMillis = now.toInstant().toEpochMilli();
138 1 1. start : Replaced long subtraction with addition → KILLED
        var startupTime = nowMillis - constants.startTime;
139
        logger.logDebug(() -> " *** Minum has finished primary startup after " + startupTime + " milliseconds ***");
140
141 1 1. start : replaced return value with null for com/renomad/minum/web/FullSystem::start → KILLED
        return this;
142
    }
143
144
    /**
145
     * this adds a hook to the Java runtime, so that if the app is running
146
     * and a user stops it - by pressing ctrl+c or a unix "kill" command - the
147
     * server socket will be shutdown and some messages about closing the server
148
     * will log
149
     */
150
    private void addShutdownHook() {
151
        shutdownHook = new Thread(ThrowingRunnable.throwingRunnableWrapper(this::shutdown, logger));
152 1 1. addShutdownHook : removed call to java/lang/Runtime::addShutdownHook → KILLED
        Runtime.getRuntime().addShutdownHook(shutdownHook);
153
    }
154
155
    /**
156
     * this saves a file to the home directory, SYSTEM_RUNNING,
157
     * that will indicate the system is active.  It can be disabled
158
     * by configuring ENABLE_SYSTEM_RUNNING_MARKER to false.
159
     */
160
    private void createSystemRunningMarker() {
161 1 1. createSystemRunningMarker : removed call to com/renomad/minum/utils/FileUtils::writeString → KILLED
        fileUtils.writeString(Path.of("SYSTEM_RUNNING"), "This file serves as a marker to indicate the system is running.\n");
162
    }
163
164
    /**
165
     * Returns current information on the non-encrypted server
166
     */
167
    public IServer getServer() {
168 1 1. getServer : replaced return value with null for com/renomad/minum/web/FullSystem::getServer → KILLED
        return server;
169
    }
170
171
    /**
172
     * Returns current information on the encrypted server
173
     */
174
    public IServer getSslServer() {
175 1 1. getSslServer : replaced return value with null for com/renomad/minum/web/FullSystem::getSslServer → KILLED
        return sslServer;
176
    }
177
178
    /**
179
     * A convenience method to get the instance registered for this property
180
     */
181
    public WebFramework getWebFramework() {
182 1 1. getWebFramework : replaced return value with null for com/renomad/minum/web/FullSystem::getWebFramework → KILLED
        return webFramework;
183
    }
184
185
    /**
186
     * A convenience method to get the instance registered for this property
187
     */
188
    public ITheBrig getTheBrig() {
189 1 1. getTheBrig : replaced return value with null for com/renomad/minum/web/FullSystem::getTheBrig → KILLED
        return theBrig;
190
    }
191
192
    /**
193
     * A convenience function to get the {@link Context} used in
194
     * this class's construction
195
     */
196
    public Context getContext() {
197 1 1. getContext : replaced return value with null for com/renomad/minum/web/FullSystem::getContext → TIMED_OUT
        return context;
198
    }
199
200
    WebEngine getWebEngine() {
201 1 1. getWebEngine : replaced return value with null for com/renomad/minum/web/FullSystem::getWebEngine → KILLED
        return webEngine;
202
    }
203
204
    /**
205
     * Shut down the system, set up to be run by a hook during initialization
206
     */
207
    public void shutdown() {
208
209 1 1. shutdown : negated conditional → KILLED
        if (!hasShutdown) {
210
            logger.logTrace(() -> "close called on " + this);
211 1 1. shutdown : removed call to com/renomad/minum/web/FullSystem::closeCore → KILLED
            closeCore(logger, context, server, sslServer, this.toString());
212
            hasShutdown = true;
213
        }
214
    }
215
216
    /**
217
     * The core code for closing resources
218
     * @param fullSystemName the name of this FullSystem, in cases where several are running concurrently
219
     */
220
    static void closeCore(ILogger logger, Context context, IServer server, IServer sslServer, String fullSystemName) {
221
        try {
222
            logger.logDebug(() -> "Received shutdown command");
223
224 1 1. closeCore : negated conditional → KILLED
            if (server != null) {
225
                logger.logDebug(() -> " Stopping the server: " + server);
226 1 1. closeCore : removed call to com/renomad/minum/web/IServer::close → KILLED
                server.close();
227
            }
228
229 1 1. closeCore : negated conditional → KILLED
            if (sslServer != null) {
230
                logger.logDebug(() -> " Stopping the SSL server: " + server);
231 1 1. closeCore : removed call to com/renomad/minum/web/IServer::close → KILLED
                sslServer.close();
232
            }
233
234
            logger.logDebug(() -> "Killing all the action queues: " + context.getActionQueueState().aqQueueAsString());
235 1 1. closeCore : removed call to com/renomad/minum/queue/ActionQueueKiller::killAllQueues → KILLED
            new ActionQueueKiller(context).killAllQueues();
236
237
            logger.logDebug(() -> String.format(
238
                    "%s %s says: Goodbye world!%n", TimeUtils.getTimestampIsoInstant(), fullSystemName));
239
240
            Files.deleteIfExists(Path.of("SYSTEM_RUNNING"));
241
        } catch (Exception e) {
242
            throw new WebServerException(e);
243
        }
244
    }
245
246
    /**
247
     * A blocking call for our multi-threaded application.
248
     * <p>
249
     * This method is needed because the entire application is
250
     * multi-threaded.  Let me help contextualize the problem
251
     * for you:
252
     * </p>
253
     * <p>
254
     *     For this application, multi-threaded means that we
255
     *     are wrapping our code in {@link Thread} classes and
256
     *     having them run using a {@link ExecutorService}.  It's
257
     *     sort of like giving instructions to someone else to carry
258
     *     out the work and sending them away, trusting the work will
259
     *     get done, rather than doing it yourself.
260
     * </p>
261
     * <p>
262
     *     But, since our entire system is done this way, once we
263
     *     have sent all our threads on their way, there's nothing
264
     *     left for us to do! Continuing the analogy, it is like
265
     *     our whole job is to give other people instructions, and
266
     *     then just wait for them to return.
267
     * </p>
268
     * <p>
269
     *     That's the purpose of this method.  It's to wait for
270
     *     the return.
271
     * </p>
272
     * <p>
273
     *     It's probably best to call this method as one of the
274
     *     last statements in the main method, so it is clear where
275
     *     execution is blocking.
276
     * </p>
277
     */
278
    public void block() {
279 1 1. block : removed call to com/renomad/minum/web/FullSystem::blockCore → KILLED
        blockCore(this.server, this.sslServer, this.logger);
280
    }
281
282
    static void blockCore(IServer server, IServer sslServer, ILogger logger) {
283
        try {
284 1 1. blockCore : negated conditional → KILLED
            if (server != null) {
285
                logger.logTrace(() -> "%s thread has properly finished startup, now waiting on the result of %s".formatted(Thread.currentThread(), server));
286
                server.getCentralLoopFuture().get();
287
            }
288 1 1. blockCore : negated conditional → KILLED
            if (sslServer != null) {
289
                logger.logTrace(() -> "%s thread has properly finished startup, now waiting on the result of %s".formatted(Thread.currentThread(), sslServer));
290
                sslServer.getCentralLoopFuture().get();
291
            }
292
        } catch (InterruptedException | ExecutionException | CancellationException ex) {
293
            Thread.currentThread().interrupt();
294
            throw new WebServerException(ex);
295
        }
296
    }
297
298
}

Mutations

62

1.1
Location : <init>
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem_EdgeCase_InstantlyClosed(com.renomad.minum.web.FullSystemTests)
removed call to com/renomad/minum/state/Context::setFullSystem → KILLED

74

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

104

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/web/FullSystem::initialize → KILLED

115

1.1
Location : start
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem_EdgeCase_InstantlyClosed(com.renomad.minum.web.FullSystemTests)
removed call to com/renomad/minum/web/FullSystem::createSystemRunningMarker → KILLED

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

118

1.1
Location : start
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem_EdgeCase_InstantlyClosed(com.renomad.minum.web.FullSystemTests)
removed call to com/renomad/minum/web/FullSystem::addShutdownHook → KILLED

121

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

138

1.1
Location : start
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem_EdgeCase_InstantlyClosed(com.renomad.minum.web.FullSystemTests)
Replaced long subtraction with addition → KILLED

141

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

152

1.1
Location : addShutdownHook
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem_EdgeCase_InstantlyClosed(com.renomad.minum.web.FullSystemTests)
removed call to java/lang/Runtime::addShutdownHook → KILLED

161

1.1
Location : createSystemRunningMarker
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem_EdgeCase_InstantlyClosed(com.renomad.minum.web.FullSystemTests)
removed call to com/renomad/minum/utils/FileUtils::writeString → KILLED

168

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

175

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

182

1.1
Location : getWebFramework
Killed by : com.renomad.minum.web.WebPerformanceTests.test3(com.renomad.minum.web.WebPerformanceTests)
replaced return value with null for com/renomad/minum/web/FullSystem::getWebFramework → KILLED

189

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

197

1.1
Location : getContext
Killed by : none
replaced return value with null for com/renomad/minum/web/FullSystem::getContext → TIMED_OUT

201

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

209

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

211

1.1
Location : shutdown
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem_EdgeCase_InstantlyClosed(com.renomad.minum.web.FullSystemTests)
removed call to com/renomad/minum/web/FullSystem::closeCore → KILLED

224

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

226

1.1
Location : closeCore
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem_EdgeCase_InstantlyClosed(com.renomad.minum.web.FullSystemTests)
removed call to com/renomad/minum/web/IServer::close → KILLED

229

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

231

1.1
Location : closeCore
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem_EdgeCase_InstantlyClosed(com.renomad.minum.web.FullSystemTests)
removed call to com/renomad/minum/web/IServer::close → KILLED

235

1.1
Location : closeCore
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem_EdgeCase_InstantlyClosed(com.renomad.minum.web.FullSystemTests)
removed call to com/renomad/minum/queue/ActionQueueKiller::killAllQueues → KILLED

279

1.1
Location : block
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem_WithRedirect(com.renomad.minum.web.FullSystemTests)
removed call to com/renomad/minum/web/FullSystem::blockCore → KILLED

284

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

288

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

Active mutators

Tests examined


Report generated by PIT 1.17.0