ServerSocial: introduce ExecutorService to manage network service thread and applicat...
authorTJ <hacker@iam.tj>
Wed, 10 Jun 2015 09:11:16 +0000 (10:11 +0100)
committerTJ <hacker@iam.tj>
Wed, 10 Jun 2015 09:11:16 +0000 (10:11 +0100)
src/uk/ac/ntu/n0521366/wsyd/server/ServerSocial.java

index 25fc039..971d185 100644 (file)
@@ -29,6 +29,9 @@ import java.io.*;
 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;
@@ -132,6 +135,11 @@ public final class ServerSocial implements NetworkMessageEventListener, Connecti
      */
     NetworkStreamManager _tcpStreamManager;
 
+    /**
+     * Thread manager
+     */
+    ExecutorService _servicePool;
+
     /**
      * Whether process should announce itself using multicast.
      * 
@@ -166,6 +174,8 @@ public final class ServerSocial implements NetworkMessageEventListener, Connecti
         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) {
@@ -193,18 +203,18 @@ public final class ServerSocial implements NetworkMessageEventListener, Connecti
         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
@@ -314,7 +324,7 @@ public final class ServerSocial implements NetworkMessageEventListener, Connecti
     @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) {
@@ -324,13 +334,19 @@ public final class ServerSocial implements NetworkMessageEventListener, Connecti
                 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;
@@ -651,12 +667,13 @@ public final class ServerSocial implements NetworkMessageEventListener, Connecti
     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");
@@ -665,6 +682,7 @@ public final class ServerSocial implements NetworkMessageEventListener, Connecti
                     } catch (InterruptedException ex) {
                         Logger.getLogger(ServerSocial.class.getName()).log(Level.SEVERE, null, ex);
                     }
+                    app = null; // allow instance to be garbage-collected
                 }
             }
         }