Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1111,58 +1111,20 @@ private void loadVersionFile() {

@Override
public void onDisable() {
if (getOptions().isJavascriptEngineEnabled()) {
getLogger().info("Shutting down Javascript engine");
JavascriptEngineHandler.getInstance().clearCachedEngine();
}

if (loadUserData && UserStorage.MYSQL.equals(getOptions().getStorageType()) && getMysql() != null) {
getMysql().close();
}
getServerDataFile().setLastUpdated();
timer.shutdown();
loginTimer.shutdown();
timeChecker.getTimer().shutdown();
inventoryTimer.shutdown();
try {
getLogger().info("Allowing background tasks to finish, this could take up to 5 seconds");
loginTimer.awaitTermination(2, TimeUnit.SECONDS);
timer.awaitTermination(2, TimeUnit.SECONDS);
timeChecker.getTimer().awaitTermination(2, TimeUnit.SECONDS);
inventoryTimer.awaitTermination(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
debug(e);
}
rewardHandler.shutdown();
loginTimer.shutdownNow();
timer.shutdownNow();
inventoryTimer.shutdownNow();
timeChecker.getTimer().shutdownNow();
onUnLoad();
getSkullCacheHandler().close();
fullInventoryHandler.save();

hologramHandler.onShutDown();

if (getPermissionHandler() != null) {
getPermissionHandler().shutDown();
}

if (dialogService != null) {
dialogService.unregister();
}

new com.bencodez.advancedcore.lifecycle.AdvancedCoreLifecycle(this).shutdown();
javaPlugin = null;
}

@Override
public void onEnable() {
javaPlugin = this;
bukkitScheduler = new BukkitScheduler(this);
timer = Executors.newSingleThreadScheduledExecutor();
loginTimer = Executors.newSingleThreadScheduledExecutor();
com.bencodez.advancedcore.lifecycle.AdvancedCoreLifecycle.RuntimeExecutors runtimeExecutors = com.bencodez.advancedcore.lifecycle.AdvancedCoreLifecycle
.createRuntimeExecutors(this);
bukkitScheduler = runtimeExecutors.getBukkitScheduler();
timer = runtimeExecutors.getTimer();
loginTimer = runtimeExecutors.getLoginTimer();
advancedCoreCommandLoader = CommandLoader.getInstance();
inventoryTimer = Executors.newSingleThreadScheduledExecutor();
inventoryTimer = runtimeExecutors.getInventoryTimer();

onPreLoad();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package com.bencodez.advancedcore.lifecycle;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import com.bencodez.advancedcore.AdvancedCorePlugin;
import com.bencodez.advancedcore.api.item.FullInventoryHandler;
import com.bencodez.advancedcore.api.javascript.JavascriptEngineHandler;
import com.bencodez.advancedcore.api.time.TimeChecker;
import com.bencodez.advancedcore.api.user.UserStorage;
import com.bencodez.simpleapi.scheduler.BukkitScheduler;

/**
* Owns AdvancedCore runtime executor creation and best-effort shutdown sequencing.
* Public plugin getters remain the compatibility surface; this class only
* centralizes lifecycle mechanics that were previously embedded in the plugin.
*/
public final class AdvancedCoreLifecycle {

private final AdvancedCorePlugin plugin;

public AdvancedCoreLifecycle(AdvancedCorePlugin plugin) {
this.plugin = plugin;
}

public static RuntimeExecutors createRuntimeExecutors(AdvancedCorePlugin plugin) {
return new RuntimeExecutors(new BukkitScheduler(plugin), Executors.newSingleThreadScheduledExecutor(),
Executors.newSingleThreadScheduledExecutor(), Executors.newSingleThreadScheduledExecutor());
}

public void shutdown() {
if (plugin == null) {
return;
}

runCleanup("Javascript engine", () -> {
if (plugin.getOptions() != null && plugin.getOptions().isJavascriptEngineEnabled()) {
plugin.getLogger().info("Shutting down Javascript engine");
JavascriptEngineHandler.getInstance().clearCachedEngine();
}
});

runCleanup("MySQL", () -> {
if (plugin.isLoadUserData() && plugin.getOptions() != null
&& UserStorage.MYSQL.equals(plugin.getOptions().getStorageType()) && plugin.getMysql() != null) {
plugin.getMysql().close();
}
});

runCleanup("server data timestamp", () -> {
if (plugin.getServerDataFile() != null) {
plugin.getServerDataFile().setLastUpdated();
}
});

ScheduledExecutorService timeTimer = null;
TimeChecker timeChecker = plugin.getTimeChecker();
if (timeChecker != null) {
timeTimer = timeChecker.getTimer();
}

shutdown(plugin.getLoginTimer());
shutdown(plugin.getTimer());
shutdown(timeTimer);
shutdown(plugin.getInventoryTimer());

plugin.getLogger().info("Allowing background tasks to finish before shutdown");
await(plugin.getLoginTimer(), 2, TimeUnit.SECONDS);
await(plugin.getTimer(), 2, TimeUnit.SECONDS);
await(timeTimer, 2, TimeUnit.SECONDS);
await(plugin.getInventoryTimer(), 1, TimeUnit.SECONDS);

runCleanup("reward handler", () -> {
if (plugin.getRewardHandler() != null) {
plugin.getRewardHandler().shutdown();
}
});

shutdownNow(plugin.getLoginTimer());
shutdownNow(plugin.getTimer());
shutdownNow(timeTimer);
shutdownNow(plugin.getInventoryTimer());

runCleanup("plugin unload hook", plugin::onUnLoad);
runCleanup("skull cache", () -> {
if (plugin.getSkullCacheHandler() != null) {
plugin.getSkullCacheHandler().close();
}
});
runCleanup("full inventory handler", () -> {
FullInventoryHandler handler = plugin.getFullInventoryHandler();
if (handler != null) {
handler.shutdown();
handler.save();
}
});
runCleanup("hologram handler", () -> {
if (plugin.getHologramHandler() != null) {
plugin.getHologramHandler().onShutDown();
}
});
runCleanup("permission handler", () -> {
if (plugin.getPermissionHandler() != null) {
plugin.getPermissionHandler().shutDown();
}
});
runCleanup("dialog service", () -> {
if (plugin.getDialogService() != null) {
plugin.getDialogService().unregister();
}
});
}

private void runCleanup(String component, Runnable cleanup) {
try {
cleanup.run();
} catch (Throwable e) {
plugin.getLogger().warning("Failed to shut down " + component + ": " + e.getMessage());
plugin.debug(e);
}
}

static void shutdown(ScheduledExecutorService executor) {
if (executor != null && !executor.isShutdown()) {
executor.shutdown();
}
}

static void shutdownNow(ScheduledExecutorService executor) {
if (executor != null && !executor.isTerminated()) {
executor.shutdownNow();
}
}

static void await(ScheduledExecutorService executor, long timeout, TimeUnit unit) {
if (executor == null) {
return;
}
try {
executor.awaitTermination(timeout, unit);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}

public static final class RuntimeExecutors {
private final BukkitScheduler bukkitScheduler;
private final ScheduledExecutorService timer;
private final ScheduledExecutorService loginTimer;
private final ScheduledExecutorService inventoryTimer;

RuntimeExecutors(BukkitScheduler bukkitScheduler, ScheduledExecutorService timer,
ScheduledExecutorService loginTimer, ScheduledExecutorService inventoryTimer) {
this.bukkitScheduler = bukkitScheduler;
this.timer = timer;
this.loginTimer = loginTimer;
this.inventoryTimer = inventoryTimer;
}

public BukkitScheduler getBukkitScheduler() {
return bukkitScheduler;
}

public ScheduledExecutorService getTimer() {
return timer;
}

public ScheduledExecutorService getLoginTimer() {
return loginTimer;
}

public ScheduledExecutorService getInventoryTimer() {
return inventoryTimer;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.bencodez.advancedcore.lifecycle;

import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import org.junit.jupiter.api.Test;

import com.bencodez.advancedcore.AdvancedCoreConfigOptions;
import com.bencodez.advancedcore.AdvancedCorePlugin;
import com.bencodez.advancedcore.api.item.FullInventoryHandler;
import com.bencodez.advancedcore.api.rewards.RewardHandler;
import com.bencodez.advancedcore.api.time.TimeChecker;
import com.bencodez.advancedcore.lifecycle.AdvancedCoreLifecycle.RuntimeExecutors;
import com.bencodez.simpleapi.scheduler.BukkitScheduler;

public class AdvancedCoreLifecycleTest {

@Test
public void runtimeExecutorsKeepExistingOwnershipBoundaries() {
BukkitScheduler bukkitScheduler = mock(BukkitScheduler.class);
ScheduledExecutorService timer = mock(ScheduledExecutorService.class);
ScheduledExecutorService loginTimer = mock(ScheduledExecutorService.class);
ScheduledExecutorService inventoryTimer = mock(ScheduledExecutorService.class);
RuntimeExecutors runtime = new RuntimeExecutors(bukkitScheduler, timer, loginTimer, inventoryTimer);

assertNotSame(runtime.getTimer(), runtime.getLoginTimer());
assertNotSame(runtime.getTimer(), runtime.getInventoryTimer());
assertNotSame(runtime.getLoginTimer(), runtime.getInventoryTimer());
}

@Test
public void shutdownCoordinatesExecutorsAndInventoryPersistence() throws Exception {
AdvancedCorePlugin plugin = mock(AdvancedCorePlugin.class);
AdvancedCoreConfigOptions options = mock(AdvancedCoreConfigOptions.class);
ScheduledExecutorService timer = mock(ScheduledExecutorService.class);
ScheduledExecutorService loginTimer = mock(ScheduledExecutorService.class);
ScheduledExecutorService inventoryTimer = mock(ScheduledExecutorService.class);
ScheduledExecutorService timeTimer = mock(ScheduledExecutorService.class);
TimeChecker timeChecker = mock(TimeChecker.class);
RewardHandler rewardHandler = mock(RewardHandler.class);
FullInventoryHandler fullInventoryHandler = mock(FullInventoryHandler.class);

when(plugin.getLogger()).thenReturn(Logger.getLogger("AdvancedCoreLifecycleTest"));
when(plugin.getOptions()).thenReturn(options);
when(plugin.getTimer()).thenReturn(timer);
when(plugin.getLoginTimer()).thenReturn(loginTimer);
when(plugin.getInventoryTimer()).thenReturn(inventoryTimer);
when(plugin.getTimeChecker()).thenReturn(timeChecker);
when(timeChecker.getTimer()).thenReturn(timeTimer);
when(plugin.getRewardHandler()).thenReturn(rewardHandler);
when(plugin.getFullInventoryHandler()).thenReturn(fullInventoryHandler);

new AdvancedCoreLifecycle(plugin).shutdown();

verify(timer).shutdown();
verify(loginTimer).shutdown();
verify(inventoryTimer).shutdown();
verify(timeTimer).shutdown();
verify(timer).awaitTermination(2, TimeUnit.SECONDS);
verify(loginTimer).awaitTermination(2, TimeUnit.SECONDS);
verify(inventoryTimer).awaitTermination(1, TimeUnit.SECONDS);
verify(timeTimer).awaitTermination(2, TimeUnit.SECONDS);
verify(rewardHandler).shutdown();
verify(fullInventoryHandler).shutdown();
verify(fullInventoryHandler).save();
verify(plugin).onUnLoad();
}
}
Loading