Server.java

1
package com.renomad.minum.web;
2
3
import com.renomad.minum.logging.ILogger;
4
import com.renomad.minum.state.Constants;
5
import com.renomad.minum.state.Context;
6
import com.renomad.minum.utils.ConcurrentSet;
7
import com.renomad.minum.utils.StacktraceUtils;
8
import com.renomad.minum.utils.ThrowingRunnable;
9
10
import java.io.IOException;
11
import java.net.ServerSocket;
12
import java.net.Socket;
13
import java.util.concurrent.BlockingQueue;
14
import java.util.concurrent.ExecutorService;
15
import java.util.concurrent.Future;
16
import java.util.concurrent.LinkedBlockingQueue;
17
18
import static com.renomad.minum.utils.ThrowingRunnable.throwingRunnableWrapper;
19
20
/**
21
 * The purpose here is to make it marginally easier to
22
 * work with a ServerSocket.
23
 * <p>
24
 * First, instantiate this class using a running serverSocket
25
 * Then, by running the start method, we gain access to
26
 * the server's socket.  This way we can easily test / control
27
 * the server side but also tie it in with an ExecutorService
28
 * for controlling lots of server threads.
29
 */
30
final class Server implements IServer {
31
    private final ServerSocket serverSocket;
32
    private final SetOfSws setOfSWs;
33
    private final ExecutorService es;
34
    private final HttpServerType serverType;
35
    private final ILogger logger;
36
    private final String serverName;
37
    private final WebFramework webFramework;
38
    private final BlockingQueue<Socket> socketQueue;
39
    private final Constants constants;
40
41
    /**
42
     * This is the future returned when we submitted the
43
     * thread for the central server loop to the ExecutorService
44
     */
45
    private Future<?> centralLoopFuture;
46
47
    Server(ServerSocket ss, Context context, String serverName, WebFramework webFramework, ExecutorService es, HttpServerType serverType) {
48
        this.serverSocket = ss;
49
        this.logger = context.getLogger();
50
        this.constants = context.getConstants();
51
        this.webFramework = webFramework;
52
        this.serverName = serverName;
53
        setOfSWs = new SetOfSws(new ConcurrentSet<>(), logger, serverName);
54
        this.es = es;
55
        this.serverType = serverType;
56
        this.socketQueue = new LinkedBlockingQueue<>();
57
    }
58
59
    @Override
60
    public void start() {
61
        ThrowingRunnable serverCode = this::outermostLoop;
62
        this.centralLoopFuture = es.submit(throwingRunnableWrapper(serverCode, logger));
63
64
        ThrowingRunnable socketHandler = this::takeOffDequeForProcessing;
65
        es.submit(throwingRunnableWrapper(socketHandler, logger));
66
    }
67
68
    /**
69
     * This code is the outermost loop of the server, waiting for incoming
70
     * connections and then delegating their handling off to a handler.
71
     */
72
    private void outermostLoop() {
73
        Thread.currentThread().setName("Main Server");
74
        try {
75
            // yes, this infinite loop can only exit by an exception.  But this is
76
            // the beating heart of a server, and to the best of my current knowledge,
77
            // when a server socket is force-closed it's going to throw an exception, and
78
            // that's just part of its life cycle
79
            //noinspection InfiniteLoopStatement
80
            while (true) {
81
                Socket freshSocket = serverSocket.accept();
82
                // see takeOffDeque for the code that pulls sockets out of this queue
83
                // and sends them for processing
84
                socketQueue.add(freshSocket);
85
            }
86
        } catch (IOException ex) {
87 1 1. outermostLoop : removed call to com/renomad/minum/web/Server::handleServerException → KILLED
            handleServerException(ex, logger);
88
        }
89
    }
90
91
    /**
92
     * An infinite loop that pulls connected sockets out of the
93
     * deque for processing
94
     */
95
    private void takeOffDequeForProcessing() throws InterruptedException {
96
        Thread.currentThread().setName("socket queue handler");
97
98
        // this is a known infinite loop, meant to keep running all during the runtime
99
        //noinspection InfiniteLoopStatement
100
        while(true) {
101
            Socket socket = socketQueue.take();
102 1 1. lambda$takeOffDequeForProcessing$0 : removed call to com/renomad/minum/web/Server::doHttpWork → KILLED
            es.submit(() -> this.doHttpWork(socket));
103
        }
104
    }
105
106
107
    void doHttpWork(Socket freshSocket) {
108
        // provide a name for this thread for easier debugging
109
        Thread.currentThread().setName("SocketWrapper thread for " + freshSocket.getInetAddress().getHostAddress());
110
111
        try {
112
            // prepare the socket for later processing
113
            ISocketWrapper socketWrapper = new SocketWrapper(freshSocket, this, logger, constants.socketTimeoutMillis, constants.hostName);
114
            logger.logTrace(() -> String.format("client connected from %s", socketWrapper.getRemoteAddrWithPort()));
115
116
            // add to a set of wrapped sockets so we can precisely close them all at shutdown
117 1 1. doHttpWork : removed call to com/renomad/minum/web/Server::addToSetOfSws → KILLED
            addToSetOfSws(socketWrapper);
118
119 1 1. doHttpWork : removed call to com/renomad/minum/web/WebFramework::httpProcessing → KILLED
            webFramework.httpProcessing(socketWrapper);
120
        } catch (Exception ex) {
121
            logger.logAsyncError(() -> StacktraceUtils.stackTraceToString(ex));
122
        }
123
    }
124
125
    static void handleServerException(IOException ex, ILogger logger) {
126 2 1. handleServerException : negated conditional → KILLED
2. handleServerException : negated conditional → KILLED
        if (!(ex.getMessage().contains("Socket closed") || ex.getMessage().contains("Socket is closed"))) {
127
            logger.logAsyncError(() -> StacktraceUtils.stackTraceToString(ex));
128
        }
129
    }
130
131
    @Override
132
    public void close() throws IOException {
133
        // close all the running sockets
134 1 1. close : removed call to com/renomad/minum/web/SetOfSws::stopAllServers → KILLED
        setOfSWs.stopAllServers();
135
        logger.logTrace(() -> "close called on " + this);
136
        // close the primary server socket
137 1 1. close : removed call to java/net/ServerSocket::close → KILLED
        serverSocket.close();
138
    }
139
140
    @Override
141
    public String getHost() {
142 1 1. getHost : replaced return value with "" for com/renomad/minum/web/Server::getHost → KILLED
        return serverSocket.getInetAddress().getHostAddress();
143
    }
144
145
    @Override
146
    public int getPort() {
147 1 1. getPort : replaced int return with 0 for com/renomad/minum/web/Server::getPort → KILLED
        return serverSocket.getLocalPort();
148
    }
149
150
    @Override
151
    public void removeMyRecord(ISocketWrapper socketWrapper) {
152 1 1. removeMyRecord : removed call to com/renomad/minum/web/SetOfSws::remove → KILLED
        setOfSWs.remove(socketWrapper);
153
    }
154
155
    @Override
156
    public void addToSetOfSws(ISocketWrapper sw) {
157 1 1. addToSetOfSws : removed call to com/renomad/minum/web/SetOfSws::add → KILLED
        this.setOfSWs.add(sw);
158
    }
159
160
    /**
161
     * Returns the name of this server, which is set
162
     * when the server is instantiated.
163
     */
164
    @Override
165
    public String toString() {
166 1 1. toString : replaced return value with "" for com/renomad/minum/web/Server::toString → KILLED
        return this.serverName;
167
    }
168
169
    @Override
170
    public Future<?> getCentralLoopFuture() {
171 1 1. getCentralLoopFuture : replaced return value with null for com/renomad/minum/web/Server::getCentralLoopFuture → KILLED
        return centralLoopFuture;
172
    }
173
174
    @Override
175
    public HttpServerType getServerType() {
176 1 1. getServerType : replaced return value with null for com/renomad/minum/web/Server::getServerType → KILLED
        return serverType;
177
    }
178
}

Mutations

87

1.1
Location : outermostLoop
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem_WithRedirect(com.renomad.minum.web.FullSystemTests)
removed call to com/renomad/minum/web/Server::handleServerException → KILLED

102

1.1
Location : lambda$takeOffDequeForProcessing$0
Killed by : com.renomad.minum.web.WebPerformanceTests.test3(com.renomad.minum.web.WebPerformanceTests)
removed call to com/renomad/minum/web/Server::doHttpWork → KILLED

117

1.1
Location : doHttpWork
Killed by : com.renomad.minum.web.ServerTests
removed call to com/renomad/minum/web/Server::addToSetOfSws → KILLED

119

1.1
Location : doHttpWork
Killed by : com.renomad.minum.web.ServerTests
removed call to com/renomad/minum/web/WebFramework::httpProcessing → KILLED

126

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

2.2
Location : handleServerException
Killed by : com.renomad.minum.web.ServerTests
negated conditional → KILLED

134

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

137

1.1
Location : close
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem_WithRedirect(com.renomad.minum.web.FullSystemTests)
removed call to java/net/ServerSocket::close → KILLED

142

1.1
Location : getHost
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem_EdgeCase_InstantlyClosed(com.renomad.minum.web.FullSystemTests)
replaced return value with "" for com/renomad/minum/web/Server::getHost → KILLED

147

1.1
Location : getPort
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem_EdgeCase_InstantlyClosed(com.renomad.minum.web.FullSystemTests)
replaced int return with 0 for com/renomad/minum/web/Server::getPort → KILLED

152

1.1
Location : removeMyRecord
Killed by : com.renomad.minum.web.ServerTests
removed call to com/renomad/minum/web/SetOfSws::remove → KILLED

157

1.1
Location : addToSetOfSws
Killed by : com.renomad.minum.web.ServerTests
removed call to com/renomad/minum/web/SetOfSws::add → KILLED

166

1.1
Location : toString
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem_EdgeCase_InstantlyClosed(com.renomad.minum.web.FullSystemTests)
replaced return value with "" for com/renomad/minum/web/Server::toString → KILLED

171

1.1
Location : getCentralLoopFuture
Killed by : com.renomad.minum.web.FullSystemTests.testFullSystem_WithRedirect(com.renomad.minum.web.FullSystemTests)
replaced return value with null for com/renomad/minum/web/Server::getCentralLoopFuture → KILLED

176

1.1
Location : getServerType
Killed by : com.renomad.minum.FunctionalTests
replaced return value with null for com/renomad/minum/web/Server::getServerType → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0