From 2ef55fdc70cdda7b57155f97da98614c8fc1d102 Mon Sep 17 00:00:00 2001 From: Obique Date: Sat, 16 May 2015 03:14:39 -0500 Subject: [PATCH] Fixed server not shutting down when using shutdown admin command; Added ShutdownService --- .../commands/callbacks/ServerCmdCallback.java | 12 +- src/resources/server_info/ObjectDatabase.java | 29 +---- src/services/CoreManager.java | 13 ++- src/services/EngineManager.java | 5 +- src/services/ShutdownService.java | 108 ++++++++++++++++++ src/utilities/ThreadUtilities.java | 13 +++ 6 files changed, 145 insertions(+), 35 deletions(-) create mode 100644 src/services/ShutdownService.java create mode 100644 src/utilities/ThreadUtilities.java diff --git a/src/resources/commands/callbacks/ServerCmdCallback.java b/src/resources/commands/callbacks/ServerCmdCallback.java index 23842dbd7..63cb440c8 100644 --- a/src/resources/commands/callbacks/ServerCmdCallback.java +++ b/src/resources/commands/callbacks/ServerCmdCallback.java @@ -139,13 +139,11 @@ public class ServerCmdCallback implements ICmdCallback { unitWindow.addItemSelectionCallback(0, new ISuiCallback() { public void handleEvent(Player player, SWGObject actor, int eventType, List returnParams) { - try { - timeUnitReference.set(unitValues[SuiListBox.getSelectedIndex(returnParams)]); - timeWindow.display(); // Ziggy: Display the next window - } catch (ArrayIndexOutOfBoundsException e) { - // Ziggy: This wasn't a valid selection - do nothing - // Should only be possible if they're using some sort of hack to alter the SUI window - } + int index = SuiListBox.getSelectedIndex(returnParams); + if (index < 0 || index >= unitValues.length) + return; + timeUnitReference.set(unitValues[index]); + timeWindow.display(); // Ziggy: Display the next window } }); diff --git a/src/resources/server_info/ObjectDatabase.java b/src/resources/server_info/ObjectDatabase.java index 2c9a51b46..2db6452fb 100644 --- a/src/resources/server_info/ObjectDatabase.java +++ b/src/resources/server_info/ObjectDatabase.java @@ -32,16 +32,16 @@ import java.io.IOException; import java.io.Serializable; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import utilities.ThreadUtilities; + public abstract class ObjectDatabase { private final File file; private final long autosaveInterval; private final ScheduledExecutorService autosaveService; private final Runnable autosaveRunnable; - private ScheduledFuture autosaveScheduled; public ObjectDatabase(String filename) { this(filename, TimeUnit.MINUTES.toMillis(5)); @@ -57,7 +57,7 @@ public abstract class ObjectDatabase { if (autosaveInterval < 60000) autosaveInterval = 60000; this.autosaveInterval = autosaveInterval; - this.autosaveService = Executors.newSingleThreadScheduledExecutor(); + this.autosaveService = Executors.newSingleThreadScheduledExecutor(ThreadUtilities.newThreadFactory("odb-autosave-"+file.getName())); this.autosaveRunnable = new Runnable() { public void run() { autosavePeriodic(); @@ -74,7 +74,7 @@ public abstract class ObjectDatabase { private void setupAutosave() { synchronized (autosaveService) { - autosaveScheduled = autosaveService.scheduleAtFixedRate(autosaveRunnable, autosaveInterval, autosaveInterval, TimeUnit.MILLISECONDS); + autosaveService.scheduleAtFixedRate(autosaveRunnable, autosaveInterval, autosaveInterval, TimeUnit.MILLISECONDS); } } @@ -99,28 +99,9 @@ public abstract class ObjectDatabase { } } - public final boolean restartAutosave() { - synchronized (autosaveService) { - if (autosaveScheduled != null) - return false; - setupAutosave(); - return true; - } - } - - public final boolean stopAutosave() { - synchronized (autosaveService) { - if (autosaveScheduled == null) - return false; - boolean success = autosaveScheduled.cancel(true); - autosaveScheduled = null; - return success; - } - } - public void close() { save(); - stopAutosave(); + autosaveService.shutdownNow(); } public final File getFile() { diff --git a/src/services/CoreManager.java b/src/services/CoreManager.java index cb6ac7928..c8e92449d 100644 --- a/src/services/CoreManager.java +++ b/src/services/CoreManager.java @@ -35,6 +35,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import network.packets.Packet; @@ -60,6 +61,7 @@ public class CoreManager extends Manager { private static final int galaxyId = 1; + private final ScheduledExecutorService shutdownService; private EngineManager engineManager; private GalacticManager galacticManager; private PrintStream packetOutput; @@ -69,6 +71,7 @@ public class CoreManager extends Manager { private boolean packetDebug; public CoreManager() { + shutdownService = Executors.newSingleThreadScheduledExecutor(); shutdownRequested = false; galaxy = getGalaxy(); if (galaxy != null) { @@ -103,6 +106,12 @@ public class CoreManager extends Manager { return galaxy != null && super.initialize(); } + @Override + public boolean terminate() { + shutdownService.shutdownNow(); + return super.terminate(); + } + @Override public void onIntentReceived(Intent i) { if (packetDebug) { @@ -135,13 +144,13 @@ public class CoreManager extends Manager { default: break; } } - + private void initiateShutdownSequence(ServerManagementIntent i) { System.out.println("Beginning server shutdown sequence..."); long time = i.getTime(); TimeUnit timeUnit = i.getTimeUnit(); - Executors.newSingleThreadScheduledExecutor().schedule( + shutdownService.schedule( new Runnable() { @Override public void run() { diff --git a/src/services/EngineManager.java b/src/services/EngineManager.java index a69301292..7180c13ad 100644 --- a/src/services/EngineManager.java +++ b/src/services/EngineManager.java @@ -37,14 +37,15 @@ import services.network.NetworkManager; public class EngineManager extends Manager { - public static final String SERVER_VERSION = "0.6"; - + private ShutdownService shutdownService; private NetworkManager networkManager; public EngineManager(Galaxy galaxy) { networkManager = new NetworkManager(galaxy); + shutdownService = new ShutdownService(); addChildService(networkManager); + addChildService(shutdownService); } @Override diff --git a/src/services/ShutdownService.java b/src/services/ShutdownService.java new file mode 100644 index 000000000..8bf868c36 --- /dev/null +++ b/src/services/ShutdownService.java @@ -0,0 +1,108 @@ +package services; + +import intents.ServerStatusIntent; +import intents.chat.ChatBroadcastIntent; +import intents.chat.ChatBroadcastIntent.BroadcastType; + +import java.util.Locale; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +import resources.control.Intent; +import resources.control.ServerStatus; +import resources.control.Service; +import utilities.ThreadUtilities; + +public class ShutdownService extends Service { + + private static final String SHUTDOWN_MESSAGE = "The server will now be shutting down."; + private static final TimeUnit BROADCAST_UNIT = TimeUnit.NANOSECONDS; + + private final AtomicLong intervalCount; + private final AtomicLong timeRemaining; + private final AtomicInteger runCount; + private ScheduledExecutorService shutdownExecutor; + + public ShutdownService() { + intervalCount = new AtomicLong(); + runCount = new AtomicInteger(); + timeRemaining = new AtomicLong(); + shutdownExecutor = null; + } + + @Override + public boolean initialize() { + registerForIntent(ServerStatusIntent.TYPE); + return super.initialize(); + } + + @Override + public boolean terminate() { + shutdownExecutor(); + return super.terminate(); + } + + public void onIntentReceived(Intent i) { + if (!(i instanceof ServerStatusIntent)) + return; + ServerStatusIntent ssi = (ServerStatusIntent) i; + if (ssi.getStatus() != ServerStatus.SHUTDOWN_REQUESTED) + return; + shutdown(ssi); + } + + private void shutdown(ServerStatusIntent i) { + final TimeUnit timeUnit = i.getTimeUnit(); + final long time = i.getTime(); + final long timeNanoSeconds = BROADCAST_UNIT.convert(time, timeUnit); + timeRemaining.set(time); + intervalCount.set((long) Math.ceil(Math.sqrt(Math.sqrt(timeNanoSeconds)) / 200)); + + if (intervalCount.get() > timeNanoSeconds) + intervalCount.set(time); + + if (time > 0) + scheduleBroadcast(timeUnit, time, timeNanoSeconds / intervalCount.get()); + else + broadcast(SHUTDOWN_MESSAGE); + } + + private void scheduleBroadcast(final TimeUnit timeUnit, final long time, final long timeBetweenBroadcasts) { + recreateExecutor(); + shutdownExecutor.scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + String message; + long timeRemainingVal = timeRemaining.get(); + + if(runCount.getAndIncrement() == intervalCount.get()) { + message = SHUTDOWN_MESSAGE; + } else { + String unitName = timeUnit.name().toLowerCase(Locale.ENGLISH); + message = String.format("The server will be shutting down in %d %s", timeRemainingVal, unitName.substring(0, unitName.length() - 1)); + message += (timeRemainingVal == 1 ? "" : "s") + "."; + timeRemaining.set(timeRemainingVal - timeUnit.convert(timeBetweenBroadcasts, BROADCAST_UNIT)); + } + broadcast(message); + } + }, 0, timeBetweenBroadcasts, BROADCAST_UNIT); + } + + private void recreateExecutor() { + shutdownExecutor(); + shutdownExecutor = Executors.newSingleThreadScheduledExecutor(ThreadUtilities.newThreadFactory("shutdown-service")); + } + + private void shutdownExecutor() { + if (shutdownExecutor != null) + shutdownExecutor.shutdownNow(); + } + + private void broadcast(String message) { + new ChatBroadcastIntent(message, BroadcastType.GALAXY).broadcast(); + } + +} diff --git a/src/utilities/ThreadUtilities.java b/src/utilities/ThreadUtilities.java new file mode 100644 index 000000000..401d935ce --- /dev/null +++ b/src/utilities/ThreadUtilities.java @@ -0,0 +1,13 @@ +package utilities; + +import java.util.concurrent.ThreadFactory; + +import org.python.google.common.util.concurrent.ThreadFactoryBuilder; + +public class ThreadUtilities { + + public static ThreadFactory newThreadFactory(String pattern) { + return new ThreadFactoryBuilder().setNameFormat(pattern).build(); + } + +}