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.io.File; | |
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 | var context = new Context(executorService, constants); | |
75 |
1
1. buildContext : removed call to com/renomad/minum/state/Context::setLogger → KILLED |
context.setLogger(logger); |
76 | ||
77 |
1
1. buildContext : replaced return value with null for com/renomad/minum/web/FullSystem::buildContext → KILLED |
return context; |
78 | } | |
79 | ||
80 | /** | |
81 | * This is the typical entry point for system instantiation. It will build | |
82 | * a {@link Context} object for you, and then properly instantiates the {@link FullSystem}. | |
83 | * <p> | |
84 | * <em>Here is an example of a simple Main file using this method:</em> | |
85 | * </p> | |
86 | * <pre>{@code | |
87 | * package org.example; | |
88 | * | |
89 | * import com.renomad.minum.web.FullSystem; | |
90 | * import com.renomad.minum.web.Response; | |
91 | * | |
92 | * import static com.renomad.minum.web.RequestLine.Method.GET; | |
93 | * | |
94 | * public class Main { | |
95 | * | |
96 | * public static void main(String[] args) { | |
97 | * FullSystem fs = FullSystem.initialize(); | |
98 | * fs.getWebFramework().registerPath(GET, "", request -> Response.htmlOk("<p>Hi there world!</p>")); | |
99 | * fs.block(); | |
100 | * } | |
101 | * } | |
102 | * }</pre> | |
103 | */ | |
104 | public static FullSystem initialize() { | |
105 | var context = buildContext(); | |
106 | var fullSystem = new FullSystem(context); | |
107 |
1
1. initialize : replaced return value with null for com/renomad/minum/web/FullSystem::initialize → KILLED |
return fullSystem.start(); |
108 | } | |
109 | ||
110 | /** | |
111 | * This method runs the necessary methods for starting the Minum | |
112 | * web server. It is unlikely you will want to use this, unless you | |
113 | * require it for more control in testing. | |
114 | * @see #initialize() | |
115 | */ | |
116 | public FullSystem start() { | |
117 | // create a file in our current working directory to indicate we are running | |
118 |
1
1. start : removed call to com/renomad/minum/web/FullSystem::createSystemRunningMarker → KILLED |
createSystemRunningMarker(); |
119 | ||
120 | // set up an action to take place if the user shuts us down | |
121 |
1
1. start : removed call to com/renomad/minum/web/FullSystem::addShutdownHook → KILLED |
addShutdownHook(); |
122 | | |
123 | // Add useful startup info to the logs | |
124 | String serverComment = "at http://" + constants.hostName + ":" + constants.serverPort + " and https://" + constants.hostName + ":" + constants.secureServerPort; | |
125 | logger.logDebug(() -> " *** Minum is starting "+serverComment+" ***"); | |
126 | | |
127 | // instantiate our security code | |
128 | theBrig = new TheBrig(context).initialize(); | |
129 | | |
130 | // the web framework handles the HTTP communications | |
131 | webFramework = new WebFramework(context); | |
132 | ||
133 | // kick off the servers - low level internet handlers | |
134 | webEngine = new WebEngine(context, webFramework); | |
135 | server = webEngine.startServer(); | |
136 | sslServer = webEngine.startSslServer(); | |
137 | ||
138 | // document how long it took to start up the system | |
139 | var now = ZonedDateTime.now(ZoneId.of("UTC")); | |
140 | var nowMillis = now.toInstant().toEpochMilli(); | |
141 |
1
1. start : Replaced long subtraction with addition → KILLED |
var startupTime = nowMillis - constants.startTime; |
142 | logger.logDebug(() -> " *** Minum has finished primary startup after " + startupTime + " milliseconds ***"); | |
143 | ||
144 |
1
1. start : replaced return value with null for com/renomad/minum/web/FullSystem::start → KILLED |
return this; |
145 | } | |
146 | ||
147 | /** | |
148 | * this adds a hook to the Java runtime, so that if the app is running | |
149 | * and a user stops it - by pressing ctrl+c or a unix "kill" command - the | |
150 | * server socket will be shutdown and some messages about closing the server | |
151 | * will log | |
152 | */ | |
153 | private void addShutdownHook() { | |
154 | shutdownHook = new Thread(ThrowingRunnable.throwingRunnableWrapper(this::shutdown, logger)); | |
155 |
1
1. addShutdownHook : removed call to java/lang/Runtime::addShutdownHook → KILLED |
Runtime.getRuntime().addShutdownHook(shutdownHook); |
156 | } | |
157 | ||
158 | /** | |
159 | * this saves a file to the home directory, SYSTEM_RUNNING, | |
160 | * that will indicate the system is active | |
161 | */ | |
162 | private void createSystemRunningMarker() { | |
163 |
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"); |
164 |
1
1. createSystemRunningMarker : removed call to java/io/File::deleteOnExit → KILLED |
new File("SYSTEM_RUNNING").deleteOnExit(); |
165 | } | |
166 | ||
167 | IServer getServer() { | |
168 |
1
1. getServer : replaced return value with null for com/renomad/minum/web/FullSystem::getServer → KILLED |
return server; |
169 | } | |
170 | ||
171 | IServer getSslServer() { | |
172 |
1
1. getSslServer : replaced return value with null for com/renomad/minum/web/FullSystem::getSslServer → KILLED |
return sslServer; |
173 | } | |
174 | ||
175 | public WebFramework getWebFramework() { | |
176 |
1
1. getWebFramework : replaced return value with null for com/renomad/minum/web/FullSystem::getWebFramework → KILLED |
return webFramework; |
177 | } | |
178 | ||
179 | public ITheBrig getTheBrig() { | |
180 |
1
1. getTheBrig : replaced return value with null for com/renomad/minum/web/FullSystem::getTheBrig → KILLED |
return theBrig; |
181 | } | |
182 | ||
183 | public Context getContext() { | |
184 |
1
1. getContext : replaced return value with null for com/renomad/minum/web/FullSystem::getContext → KILLED |
return context; |
185 | } | |
186 | ||
187 | WebEngine getWebEngine() { | |
188 |
1
1. getWebEngine : replaced return value with null for com/renomad/minum/web/FullSystem::getWebEngine → KILLED |
return webEngine; |
189 | } | |
190 | ||
191 | public void shutdown() { | |
192 | ||
193 |
1
1. shutdown : negated conditional → KILLED |
if (!hasShutdown) { |
194 | logger.logTrace(() -> "close called on " + this); | |
195 |
1
1. shutdown : removed call to com/renomad/minum/web/FullSystem::closeCore → KILLED |
closeCore(logger, context, server, sslServer, this.toString()); |
196 | hasShutdown = true; | |
197 | } | |
198 | } | |
199 | ||
200 | /** | |
201 | * The core code for closing resources | |
202 | * @param fullSystemName the name of this FullSystem, in cases where several are running concurrently | |
203 | */ | |
204 | static void closeCore(ILogger logger, Context context, IServer server, IServer sslServer, String fullSystemName) { | |
205 | try { | |
206 | logger.logDebug(() -> "Received shutdown command"); | |
207 | logger.logDebug(() -> " Stopping the server: " + server); | |
208 |
1
1. closeCore : removed call to com/renomad/minum/web/IServer::close → KILLED |
server.close(); |
209 | logger.logDebug(() -> " Stopping the SSL server: " + server); | |
210 |
1
1. closeCore : removed call to com/renomad/minum/web/IServer::close → TIMED_OUT |
sslServer.close(); |
211 | logger.logDebug(() -> "Killing all the action queues: " + context.getActionQueueState().aqQueueAsString()); | |
212 |
1
1. closeCore : removed call to com/renomad/minum/queue/ActionQueueKiller::killAllQueues → KILLED |
new ActionQueueKiller(context).killAllQueues(); |
213 | logger.logDebug(() -> String.format( | |
214 | "%s %s says: Goodbye world!%n", TimeUtils.getTimestampIsoInstant(), fullSystemName)); | |
215 | } catch (Exception e) { | |
216 | throw new WebServerException(e); | |
217 | } | |
218 | } | |
219 | ||
220 | /** | |
221 | * A blocking call for our multi-threaded application. | |
222 | * <p> | |
223 | * This method is needed because the entire application is | |
224 | * multi-threaded. Let me help contextualize the problem | |
225 | * for you: | |
226 | * </p> | |
227 | * <p> | |
228 | * For this application, multi-threaded means that we | |
229 | * are wrapping our code in {@link Thread} classes and | |
230 | * having them run using a {@link ExecutorService}. It's | |
231 | * sort of like giving instructions to someone else to carry | |
232 | * out the work and sending them away, trusting the work will | |
233 | * get done, rather than doing it yourself. | |
234 | * </p> | |
235 | * <p> | |
236 | * But, since our entire system is done this way, once we | |
237 | * have sent all our threads on their way, there's nothing | |
238 | * left for us to do! Continuing the analogy, it is like | |
239 | * our whole job is to give other people instructions, and | |
240 | * then just wait for them to return. | |
241 | * </p> | |
242 | * <p> | |
243 | * That's the purpose of this method. It's to wait for | |
244 | * the return. | |
245 | * </p> | |
246 | * <p> | |
247 | * It's probably best to call this method as one of the | |
248 | * last statements in the main method, so it is clear where | |
249 | * execution is blocking. | |
250 | * </p> | |
251 | */ | |
252 | public void block() { | |
253 |
1
1. block : removed call to com/renomad/minum/web/FullSystem::blockCore → KILLED |
blockCore(this.server, this.sslServer); |
254 | } | |
255 | ||
256 | static void blockCore(IServer server, IServer sslServer) { | |
257 | try { | |
258 | server.getCentralLoopFuture().get(); | |
259 | sslServer.getCentralLoopFuture().get(); | |
260 | } catch (InterruptedException | ExecutionException | CancellationException ex) { | |
261 | Thread.currentThread().interrupt(); | |
262 | throw new WebServerException(ex); | |
263 | } | |
264 | } | |
265 | ||
266 | /** | |
267 | * Intentionally return just the default object toString, this is only used | |
268 | * to differentiate between multiple instances in memory. | |
269 | */ | |
270 | @Override | |
271 | public String toString() { | |
272 |
1
1. toString : replaced return value with "" for com/renomad/minum/web/FullSystem::toString → KILLED |
return super.toString(); |
273 | } | |
274 | } | |
Mutations | ||
62 |
1.1 |
|
75 |
1.1 |
|
77 |
1.1 |
|
107 |
1.1 |
|
118 |
1.1 |
|
121 |
1.1 |
|
141 |
1.1 |
|
144 |
1.1 |
|
155 |
1.1 |
|
163 |
1.1 |
|
164 |
1.1 |
|
168 |
1.1 |
|
172 |
1.1 |
|
176 |
1.1 |
|
180 |
1.1 |
|
184 |
1.1 |
|
188 |
1.1 |
|
193 |
1.1 |
|
195 |
1.1 |
|
208 |
1.1 |
|
210 |
1.1 |
|
212 |
1.1 |
|
253 |
1.1 |
|
272 |
1.1 |