import java.net.InetSocketAddress;
import java.text.MessageFormat;
import java.util.*;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
import java.util.logging.*;
import javax.swing.Timer;
import uk.ac.ntu.n0521366.wsyd.libs.WSYD_Member;
*/
NetworkStreamManager _tcpStreamManager;
+ /**
+ * Thread manager
+ */
+ ExecutorService _servicePool;
+
/**
* Whether process should announce itself using multicast.
*
readMembers(_membersFile);
_membersOnline = new ArrayList<>();
_tcpStreamManager = new NetworkStreamManager();
+ // FIXME: increase size of thread pool if more background services are added
+ _servicePool = Executors.newFixedThreadPool(3);
}
public ServerSocial(boolean multicastAnnouncements) {
WSYD_SocketAddress _multicastAdvertiserSA = new WSYD_SocketAddress(Network.MULTICAST_IPv4, Network.PORTS_MULTICAST_DISCOVERY, WSYD_SocketAddress.Protocol.UDP);
_multicastService = new NetworkServerUDPMulticast(_multicastAdvertiserSA, _title + "MC", _serviceToAddressMap, LOGGER);
_multicastService.getEventManager().addNetworkMessageEventListener(this, "Neighbour");
- _multicastService.execute();
+ _servicePool.execute(_multicastService);
_serviceToAddressMap.put("all", new ServiceAddressMap.LastSeenHost(new InetSocketAddress(Network.MULTICAST_IPv4, Network.PORTS_MULTICAST_DISCOVERY), ServiceAddressMap.LastSeenHost.STATE.STATIC));
WSYD_SocketAddress _udpControlServiceSA = new WSYD_SocketAddress(Network.IPv4_WILDCARD, Network.PORTS_SERVER_CONTROL, WSYD_SocketAddress.Protocol.UDP);
_udpControlService = new NetworkServerUDP(_udpControlServiceSA, _title + "Control", _serviceToAddressMap, LOGGER);
_udpControlService.getEventManager().addNetworkMessageEventListener(this, "Control");
- _udpControlService.execute();
+ _servicePool.execute(_udpControlService);
WSYD_SocketAddress _tcpListeningServiceSA = new WSYD_SocketAddress(Network.IPv4_WILDCARD, Network.PORTS_SERVER_SOCIAL, WSYD_SocketAddress.Protocol.TCP);
_tcpListeningService = new NetworkServerTCP(_tcpListeningServiceSA, _title + "Listener", _serviceToAddressMap, _tcpStreamManager, LOGGER);
_tcpListeningService.addConnectionEstablisedEventListener(this);
- _tcpListeningService.execute();
+ _servicePool.execute(_tcpListeningService);
ActionListener regularTasksActionListener = new ActionListener() {
// provide a way to read the current version of the parent process object's fields
@SuppressWarnings("SleepWhileInLoop")
public boolean start() throws InterruptedException {
boolean result;
- Logger.getLogger(ServerSocial.class.getName()).log(Level.SEVERE, "Starting main loop");
+ logp(Level.SEVERE, "Starting main loop");
// wait for connections
int loopCount = 200;
while (!ServerSocial.exitRequested && !ServerSocial.restartRequested) {
ServerSocial.exitRequested = true;
}
}
- Logger.getLogger(ServerSocial.class.getName()).log(Level.SEVERE, "Stopping main loop");
+ logp(Level.SEVERE, "Stopping main loop");
_regularTasks.stop();
- _multicastService.cancel(true);
- _udpControlService.cancel(true);
- _tcpListeningService.cancel(true);
-
+ _multicastService.cancel(false);
+ _udpControlService.cancel(false);
+ _tcpListeningService.cancel(false);
+ _tcpStreamManager.closeAll();
+ _servicePool.shutdown();
+ while (!_servicePool.isTerminated()) {
+ logp(Level.SEVERE, "Waiting for background threads to terminate");
+ _servicePool.awaitTermination(100, TimeUnit.MILLISECONDS);
+ }
+ logp(Level.SEVERE, "Background threads terminated");
result = writeMembers(_membersFile);
return result;
public static void main(String[] args) {
class App implements Runnable {
public boolean multicastAnnouncements = true;
+ ServerSocial app;
@Override
public void run() {
while (!ServerSocial.exitRequested && ServerSocial.restartRequested) {
- ServerSocial app = new ServerSocial(multicastAnnouncements).initListeners();
ServerSocial.restartRequested = false;
+ app = new ServerSocial(multicastAnnouncements).initListeners();
try {
if (!app.start()) {
Logger.getLogger(ServerSocial.class.getName()).log(Level.SEVERE, null, "Encountered error running Social Server");
} catch (InterruptedException ex) {
Logger.getLogger(ServerSocial.class.getName()).log(Level.SEVERE, null, ex);
}
+ app = null; // allow instance to be garbage-collected
}
}
}