From 7c97a6ec57498257a7fcbf24d3e7dcc51fd23581 Mon Sep 17 00:00:00 2001 From: Obique Date: Sat, 16 May 2015 20:16:01 -0500 Subject: [PATCH] Added client disconnect when server shuts down --- src/main/ProjectSWG.java | 24 ++++++++++- src/resources/control/IntentManager.java | 41 +++++++++++-------- src/resources/control/Manager.java | 23 +++++++++++ src/resources/control/ServerStatus.java | 1 + src/resources/control/Service.java | 10 ++++- src/services/CoreManager.java | 6 +++ .../network/NetworkClientManager.java | 7 +++- 7 files changed, 90 insertions(+), 22 deletions(-) diff --git a/src/main/ProjectSWG.java b/src/main/ProjectSWG.java index ca9bfb7a0..dd6f1d302 100644 --- a/src/main/ProjectSWG.java +++ b/src/main/ProjectSWG.java @@ -29,6 +29,7 @@ package main; import intents.ServerStatusIntent; import resources.Galaxy.GalaxyStatus; +import resources.control.IntentManager; import resources.control.ServerStatus; import services.CoreManager; @@ -55,6 +56,7 @@ public class ProjectSWG { e.printStackTrace(); System.err.println("ProjectSWG: Shutting down - unknown error."); } + server.stop(); server.terminate(); System.out.println("ProjectSWG: Server shut down."); } @@ -78,6 +80,7 @@ public class ProjectSWG { initialize(); start(); loop(); + stop(); terminate(); if (!shutdownRequested && !manager.isShutdownRequested()) { manager = new CoreManager(); @@ -123,10 +126,29 @@ public class ProjectSWG { } } + private void stop() { + if (manager == null || status == ServerStatus.OFFLINE) + return; + System.out.println("ProjectSWG: Stopping..."); + setStatus(ServerStatus.STOPPING); + if (!manager.stop()) + System.err.println("Failed to stop."); + long intentWait = System.nanoTime(); + while (IntentManager.getIntentsQueued() > 0 && System.nanoTime()-intentWait < 3E9) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + System.err.println("ProjectSWG: Failed to stop! Interrupted with " + IntentManager.getIntentsQueued() + " intents remaining"); + break; + } + } + System.out.println("ProjectSWG: Stopped. Time: " + manager.getCoreTime() + "ms"); + } + private void terminate() { if (manager == null || status == ServerStatus.OFFLINE) return; - System.out.println("ProjectSWG: Shutting down server..."); + System.out.println("ProjectSWG: Terminating..."); setStatus(ServerStatus.TERMINATING); if (!manager.terminate()) throw new CoreException("Failed to terminate."); diff --git a/src/resources/control/IntentManager.java b/src/resources/control/IntentManager.java index 5d465f80e..456bcbec8 100644 --- a/src/resources/control/IntentManager.java +++ b/src/resources/control/IntentManager.java @@ -27,6 +27,8 @@ ***********************************************************************************/ package resources.control; +import intents.ServerStatusIntent; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -36,15 +38,11 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.TimeUnit; import utilities.ThreadUtilities; -/** - * Don't you dare make this public. - */ -class IntentManager { +public class IntentManager { private static final IntentManager instance = new IntentManager(); private final Runnable broadcastRunnable; @@ -54,18 +52,20 @@ class IntentManager { private boolean initialized = false; private boolean terminated = false; - public IntentManager() { + private IntentManager() { initialize(); broadcastRunnable = new Runnable() { public void run() { Intent i = intentQueue.poll(); if (i != null) broadcast(i); + if (i instanceof ServerStatusIntent) + onServerStatusIntent((ServerStatusIntent) i); } }; } - public void initialize() { + protected void initialize() { if (!initialized) { broadcastThreads = Executors.newCachedThreadPool(ThreadUtilities.newThreadFactory("intent-processor-%d")); intentRegistrations = new HashMap>(); @@ -75,20 +75,21 @@ class IntentManager { } } - public void terminate() { + private void terminate() { if (!terminated) { - broadcastThreads.shutdownNow(); - try { - broadcastThreads.awaitTermination(5, TimeUnit.SECONDS); - } catch (InterruptedException e) { - e.printStackTrace(); - } + broadcastThreads.shutdown(); initialized = false; terminated = true; } } - public void broadcastIntent(Intent i) { + private void onServerStatusIntent(ServerStatusIntent i) { + if (i.getStatus() == ServerStatus.TERMINATING) { + terminate(); + } + } + + protected void broadcastIntent(Intent i) { if (i == null) throw new NullPointerException("Intent cannot be null!"); intentQueue.add(i); @@ -96,7 +97,7 @@ class IntentManager { catch (RejectedExecutionException e) { } // This error is thrown when the server is being shut down } - public void registerForIntent(String intentType, IntentReceiver r) { + protected void registerForIntent(String intentType, IntentReceiver r) { if (r == null) throw new NullPointerException("Cannot register a null value for an intent"); synchronized (intentRegistrations) { @@ -111,7 +112,7 @@ class IntentManager { } } - public void unregisterForIntent(String intentType, IntentReceiver r) { + protected void unregisterForIntent(String intentType, IntentReceiver r) { if (r == null) return; synchronized (intentRegistrations) { @@ -152,7 +153,11 @@ class IntentManager { } } - public static IntentManager getInstance() { + public static int getIntentsQueued() { + return getInstance().intentQueue.size(); + } + + protected static IntentManager getInstance() { return instance; } diff --git a/src/resources/control/Manager.java b/src/resources/control/Manager.java index b13141189..2ea1ba87c 100644 --- a/src/resources/control/Manager.java +++ b/src/resources/control/Manager.java @@ -98,6 +98,29 @@ public class Manager extends Service { return success; } + /** + * Stops this manager. If the manager returns false on this method then + * the manger failed to stop and may not have fully locked down. This will + * start all children automatically. + * @return TRUE if stopping was successful, FALSE otherwise + */ + @Override + public boolean stop() { + boolean success = super.stop(), cSuccess = true; + synchronized (children) { + for (Service child : children) { + if (!success) + break; + cSuccess = child.stop(); + if (!cSuccess) { + System.err.println(child.getClass().getSimpleName() + " failed to stop!"); + success = false; + } + } + } + return success; + } + /** * Terminates this manager. If the manager returns false on this method * then the manager failed to shut down and resources may not have been diff --git a/src/resources/control/ServerStatus.java b/src/resources/control/ServerStatus.java index 5d8249c50..3d29d126d 100644 --- a/src/resources/control/ServerStatus.java +++ b/src/resources/control/ServerStatus.java @@ -33,5 +33,6 @@ public enum ServerStatus { OPEN, LOCKED, SHUTDOWN_REQUESTED, + STOPPING, TERMINATING; } diff --git a/src/resources/control/Service.java b/src/resources/control/Service.java index 89852d9c5..de5c0fa52 100644 --- a/src/resources/control/Service.java +++ b/src/resources/control/Service.java @@ -62,6 +62,15 @@ public class Service implements IntentReceiver { return DataManager.getInstance().isInitialized(); } + /** + * Stops the service. If the service returns false on this method then the + * service failed to stop and may not have fully locked down. + * @return TRUE if stopping was successful, FALSe otherwise + */ + public boolean stop() { + return true; + } + /** * Terminates this service. If the service returns false on this method * then the service failed to shut down and resources may not have been @@ -69,7 +78,6 @@ public class Service implements IntentReceiver { * @return TRUE if termination was successful, FALSE otherwise */ public boolean terminate() { - IntentManager.getInstance().terminate(); return ServerManager.getInstance().terminate(); } diff --git a/src/services/CoreManager.java b/src/services/CoreManager.java index c8e92449d..b449f6dd5 100644 --- a/src/services/CoreManager.java +++ b/src/services/CoreManager.java @@ -106,6 +106,12 @@ public class CoreManager extends Manager { return galaxy != null && super.initialize(); } + @Override + public boolean stop() { + galaxy.setStatus(GalaxyStatus.LOCKED); + return super.stop(); + } + @Override public boolean terminate() { shutdownService.shutdownNow(); diff --git a/src/services/network/NetworkClientManager.java b/src/services/network/NetworkClientManager.java index aecab048a..d3b9df0d0 100644 --- a/src/services/network/NetworkClientManager.java +++ b/src/services/network/NetworkClientManager.java @@ -76,8 +76,11 @@ public class NetworkClientManager extends Manager { } @Override - public boolean terminate() { - return super.terminate(); + public boolean stop() { + for (NetworkClient client : networkClients.values()) { + client.sendPacket(new Disconnect(client.getConnectionId(), DisconnectReason.APPLICATION)); + } + return super.stop(); } public void onIntentReceived(Intent i) {