Changed from switching between File & Path to just using Path, revamped InitialDependencyLoader

This commit is contained in:
Vankka 2021-08-07 18:42:54 +03:00
parent 7d14a92158
commit 3fb79cc50c
No known key found for this signature in database
GPG Key ID: 6E50CB7A29B96AD0
13 changed files with 169 additions and 68 deletions

View File

@ -30,7 +30,6 @@ import com.discordsrv.bukkit.scheduler.BukkitScheduler;
import com.discordsrv.common.config.manager.ConnectionConfigManager;
import com.discordsrv.common.config.manager.MainConfigManager;
import com.discordsrv.common.logging.logger.Logger;
import com.discordsrv.common.logging.logger.impl.JavaLoggerImpl;
import com.discordsrv.common.server.ServerDiscordSRV;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.bukkit.Server;
@ -54,10 +53,10 @@ public class BukkitDiscordSRV extends ServerDiscordSRV<BukkitConfig, BukkitConne
private final BukkitConnectionConfigManager connectionConfigManager;
private final BukkitConfigManager configManager;
public BukkitDiscordSRV(DiscordSRVBukkitBootstrap bootstrap) {
public BukkitDiscordSRV(DiscordSRVBukkitBootstrap bootstrap, Logger logger) {
this.bootstrap = bootstrap;
this.logger = logger;
this.logger = new JavaLoggerImpl(bootstrap.getPlugin().getLogger());
this.dataDirectory = bootstrap.getPlugin().getDataFolder().toPath();
this.scheduler = new BukkitScheduler(this);
this.console = new BukkitConsole(this);
@ -66,6 +65,8 @@ public class BukkitDiscordSRV extends ServerDiscordSRV<BukkitConfig, BukkitConne
// Config
this.connectionConfigManager = new BukkitConnectionConfigManager(this);
this.configManager = new BukkitConfigManager(this);
load();
}
public JavaPlugin plugin() {

View File

@ -19,6 +19,8 @@
package com.discordsrv.bukkit;
import com.discordsrv.common.dependency.InitialDependencyLoader;
import com.discordsrv.common.logging.logger.Logger;
import com.discordsrv.common.logging.logger.impl.JavaLoggerImpl;
import dev.vankka.mcdependencydownload.bukkit.bootstrap.BukkitBootstrap;
import dev.vankka.mcdependencydownload.classloader.JarInJarClassLoader;
import org.bukkit.plugin.java.JavaPlugin;
@ -27,29 +29,35 @@ import java.io.IOException;
public class DiscordSRVBukkitBootstrap extends BukkitBootstrap {
private final Logger logger;
private final InitialDependencyLoader dependencies;
private BukkitDiscordSRV discordSRV;
public DiscordSRVBukkitBootstrap(JarInJarClassLoader classLoader, JavaPlugin plugin) throws IOException {
// Don't change these parameters
super(classLoader, plugin);
this.logger = new JavaLoggerImpl(plugin.getLogger());
this.dependencies = new InitialDependencyLoader(
logger,
plugin.getDataFolder().toPath(),
new String[] {"dependencies/runtimeDownloadApi-bukkit.txt"},
getClasspathAppender()
);
dependencies.whenComplete(() -> this.discordSRV = new BukkitDiscordSRV(this));
}
@Override
public void onEnable() {
dependencies.whenComplete(() -> discordSRV.invokeEnable());
// Wait until dependencies ready, then initialize DiscordSRV
dependencies.join();
this.discordSRV = new BukkitDiscordSRV(this, logger);
dependencies.runWhenComplete(() -> discordSRV.invokeEnable());
getPlugin().getServer().getScheduler().runTaskLater(getPlugin(),
() -> dependencies.whenComplete(() -> discordSRV.invokeServerStarted()), 1L);
() -> dependencies.runWhenComplete(() -> discordSRV.invokeServerStarted()), 1L);
}
@Override
public void onDisable() {
dependencies.whenComplete(() -> discordSRV.invokeDisable());
dependencies.runWhenComplete(() -> discordSRV.invokeDisable());
}
}

View File

@ -25,7 +25,6 @@ import com.discordsrv.common.config.main.MainConfig;
import com.discordsrv.common.config.manager.ConnectionConfigManager;
import com.discordsrv.common.config.manager.MainConfigManager;
import com.discordsrv.common.logging.logger.Logger;
import com.discordsrv.common.logging.logger.impl.JavaLoggerImpl;
import com.discordsrv.common.proxy.ProxyDiscordSRV;
import com.discordsrv.common.scheduler.StandardScheduler;
import net.kyori.adventure.platform.bungeecord.BungeeAudiences;
@ -46,14 +45,16 @@ public class BungeeDiscordSRV extends ProxyDiscordSRV<MainConfig, ConnectionConf
private final BungeeConsole console;
private final BungeePlayerProvider playerProvider;
public BungeeDiscordSRV(DiscordSRVBungeeBootstrap bootstrap) {
public BungeeDiscordSRV(DiscordSRVBungeeBootstrap bootstrap, Logger logger) {
this.bootstrap = bootstrap;
this.logger = logger;
this.logger = new JavaLoggerImpl(bootstrap.getPlugin().getLogger());
this.dataDirectory = bootstrap.getPlugin().getDataFolder().toPath();
this.scheduler = new StandardScheduler(this);
this.console = new BungeeConsole(this);
this.playerProvider = new BungeePlayerProvider(this);
load();
}
public Plugin plugin() {

View File

@ -19,6 +19,8 @@
package com.discordsrv.bungee;
import com.discordsrv.common.dependency.InitialDependencyLoader;
import com.discordsrv.common.logging.logger.Logger;
import com.discordsrv.common.logging.logger.impl.JavaLoggerImpl;
import dev.vankka.mcdependencydownload.bungee.bootstrap.BungeeBootstrap;
import dev.vankka.mcdependencydownload.classloader.JarInJarClassLoader;
import net.md_5.bungee.api.plugin.Plugin;
@ -27,27 +29,33 @@ import java.io.IOException;
public class DiscordSRVBungeeBootstrap extends BungeeBootstrap {
private final Logger logger;
private final InitialDependencyLoader dependencies;
private BungeeDiscordSRV discordSRV;
public DiscordSRVBungeeBootstrap(JarInJarClassLoader classLoader, Plugin plugin) throws IOException {
// Don't change these parameters
super(classLoader, plugin);
this.logger = new JavaLoggerImpl(plugin.getLogger());
this.dependencies = new InitialDependencyLoader(
logger,
plugin.getDataFolder().toPath(),
new String[] {"dependencies/runtimeDownloadApi-bungee.txt"},
getClasspathAppender()
);
dependencies.whenComplete(() -> this.discordSRV = new BungeeDiscordSRV(this));
}
@Override
public void onEnable() {
dependencies.whenComplete(discordSRV::invokeEnable);
// Wait until dependencies ready, then initialize DiscordSRV
dependencies.join();
this.discordSRV = new BungeeDiscordSRV(this, logger);
dependencies.runWhenComplete(discordSRV::invokeEnable);
}
@Override
public void onDisable() {
dependencies.whenComplete(discordSRV::invokeDisable);
dependencies.runWhenComplete(discordSRV::invokeDisable);
}
}

View File

@ -37,7 +37,7 @@ public abstract class ServerDiscordSRV<C extends MainConfig, CC extends Connecti
public abstract @NotNull ServerPlayerProvider<?> playerProvider();
public final CompletableFuture<Void> invokeServerStarted() {
return invoke(this::serverStarted, "Failed to enable", true);
return invokeLifecycle(this::serverStarted, "Failed to enable", true);
}
@OverridingMethodsMustInvokeSuper

View File

@ -49,17 +49,24 @@ import java.util.Locale;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
/**
* DiscordSRV's implementation's common code.
* Implementations of this class must call {@link #load()} at the end of their constructors.
* @param <C> the config type
* @param <CC> the connections config type
*/
public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends ConnectionConfig> implements DiscordSRV {
private final AtomicReference<Status> status = new AtomicReference<>(Status.INITIALIZED);
// DiscordSRVApi
private final EventBus eventBus;
private EventBus eventBus;
private PlaceholderService placeholderService;
private final ComponentFactory componentFactory;
private ComponentFactory componentFactory;
private DiscordAPIImpl discordAPI;
private final DiscordConnectionDetails discordConnectionDetails;
private DiscordConnectionDetails discordConnectionDetails;
// DiscordSRV
private final DefaultGlobalChannel defaultGlobalChannel = new DefaultGlobalChannel(this);
@ -67,12 +74,18 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
private DiscordConnectionManager discordConnectionManager;
// Internal
private final ReentrantLock lifecycleLock = new ReentrantLock();
private final DependencyLoggingFilter dependencyLoggingFilter = new DependencyLoggingFilter(this);
public AbstractDiscordSRV() {
ApiInstanceUtil.setInstance(this);
}
protected final void load() {
this.eventBus = new EventBusImpl(this);
this.placeholderService = new PlaceholderServiceImpl(this);
this.componentFactory = new ComponentFactory();
this.discordAPI = new DiscordAPIImpl(this);
this.discordConnectionDetails = new DiscordConnectionDetailsImpl(this);
}
@ -159,6 +172,17 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
this.status.set(status);
}
protected CompletableFuture<Void> invokeLifecycle(CheckedRunnable runnable, String message, boolean enable) {
return invoke(() -> {
try {
lifecycleLock.lock();
runnable.run();
} finally {
lifecycleLock.unlock();
}
}, message, enable);
}
protected CompletableFuture<Void> invoke(CheckedRunnable runnable, String message, boolean enable) {
return CompletableFuture.runAsync(() -> {
try {
@ -175,12 +199,12 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
@Override
public final CompletableFuture<Void> invokeEnable() {
return invoke(this::enable, "Failed to enable", true);
return invokeLifecycle(this::enable, "Failed to enable", true);
}
@Override
public final CompletableFuture<Void> invokeDisable() {
return invoke(this::disable, "Failed to disable", false);
return invokeLifecycle(this::disable, "Failed to disable", false);
}
@Override
@ -190,9 +214,11 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
@OverridingMethodsMustInvokeSuper
protected void enable() throws Throwable {
// API Stuff
this.placeholderService = new PlaceholderServiceImpl(this);
this.discordAPI = new DiscordAPIImpl(this);
if (eventBus == null) {
// Error that should only occur with new platforms
throw new IllegalStateException("AbstractDiscordSRV#load was not called from the end of "
+ getClass().getName() + " constructor");
}
// Config
try {
@ -225,8 +251,8 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
@OverridingMethodsMustInvokeSuper
protected void disable() {
Status status = this.status.get();
if (status.isShutdown()) {
// Already shutting down/shutdown
if (status == Status.INITIALIZED || status.isShutdown()) {
// Hasn't started or already shutting down/shutdown
return;
}
this.status.set(Status.SHUTTING_DOWN);

View File

@ -32,7 +32,6 @@ import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.ObjectMapper;
import org.spongepowered.configurate.serialize.SerializationException;
import java.io.File;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
@ -51,7 +50,7 @@ public abstract class ConfigurateConfigManager<T, LT extends AbstractConfigurati
public ConfigurateConfigManager(DiscordSRV discordSRV) {
this.discordSRV = discordSRV;
this.filePath = new File(discordSRV.dataDirectory().toFile(), fileName()).toPath();
this.filePath = discordSRV.dataDirectory().resolve(fileName());
this.configObjectMapper = configObjectMapperBuilder().build();
this.defaultObjectMapper = defaultObjectMapperBuilder().build();
this.loader = createLoader(filePath, configNodeOptions());

View File

@ -23,7 +23,6 @@ import dev.vankka.dependencydownload.DependencyManager;
import dev.vankka.dependencydownload.classpath.ClasspathAppender;
import dev.vankka.dependencydownload.repository.StandardRepository;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays;
@ -58,7 +57,7 @@ public class DependencyLoader {
Executor executor,
String[] dependencyResources
) {
this.cacheDirectory = new File(dataDirectory.toFile(), "cache").toPath();
this.cacheDirectory = dataDirectory.resolve("cache");
this.executor = executor;
this.dependencyResources = dependencyResources;
}

View File

@ -18,6 +18,7 @@
package com.discordsrv.common.dependency;
import com.discordsrv.common.logging.logger.Logger;
import com.discordsrv.common.scheduler.threadfactory.CountingForkJoinWorkerThreadFactory;
import dev.vankka.dependencydownload.classpath.ClasspathAppender;
@ -27,25 +28,27 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ForkJoinPool;
/**
* TODO: revamp
* - run DiscordSRV#load() after DiscordSRV is initialized
* - catch exceptions, so they don't go missing
* - make the whenComplete stuff less janky
*/
public class InitialDependencyLoader {
private CompletableFuture<?> completableFuture;
protected ForkJoinPool taskPool;
private final ForkJoinPool taskPool;
private final CompletableFuture<?> completableFuture;
private final List<Runnable> tasks = new CopyOnWriteArrayList<>();
public InitialDependencyLoader(
Logger logger,
Path dataDirectory,
String[] dependencyResources,
ClasspathAppender classpathAppender
) throws IOException {
this.taskPool = new ForkJoinPool(Runtime.getRuntime().availableProcessors(), new CountingForkJoinWorkerThreadFactory("DiscordSRV initial dependency download #%s"), null, false);
this.taskPool = new ForkJoinPool(
Runtime.getRuntime().availableProcessors(),
new CountingForkJoinWorkerThreadFactory("DiscordSRV Initialization #%s"),
null,
false
);
List<String> resourcePaths = new ArrayList<>(Arrays.asList(
"dependencies/runtimeDownloadOnly-common.txt",
@ -58,11 +61,42 @@ public class InitialDependencyLoader {
taskPool,
resourcePaths.toArray(new String[0])
);
this.completableFuture = dependencyLoader.process(classpathAppender);
whenComplete(() -> taskPool.shutdown());
completableFuture.whenComplete((v, t) -> {
if (t != null) {
logger.error("Error loading dependencies", t);
return;
}
for (Runnable task : tasks) {
try {
task.run();
} catch (Throwable throwable) {
logger.error("Callback failed", throwable);
}
}
taskPool.shutdown();
});
}
public CompletableFuture<?> whenComplete(Runnable runnable) {
return this.completableFuture = completableFuture.whenComplete((v, t) -> runnable.run());
/**
* Joins the dependency download.
*/
public void join() {
completableFuture.join();
}
/**
* This will run on the current thread if dependencies are already downloaded, otherwise will be added to a list.
*/
public void runWhenComplete(Runnable runnable) {
if (completableFuture.isDone()) {
runnable.run();
return;
}
tasks.add(runnable);
}
}

View File

@ -19,6 +19,8 @@
package com.discordsrv.sponge;
import com.discordsrv.common.dependency.InitialDependencyLoader;
import com.discordsrv.common.logging.logger.Logger;
import com.discordsrv.common.logging.logger.impl.Log4JLoggerImpl;
import com.discordsrv.sponge.bootstrap.ISpongeBootstrap;
import dev.vankka.mcdependencydownload.bootstrap.AbstractBootstrap;
import dev.vankka.mcdependencydownload.bootstrap.classpath.JarInJarClasspathAppender;
@ -32,44 +34,52 @@ import java.nio.file.Path;
@SuppressWarnings("unused") // Reflection
public class DiscordSRVSpongeBootstrap extends AbstractBootstrap implements ISpongeBootstrap {
private final Logger logger;
private final InitialDependencyLoader dependencies;
private SpongeDiscordSRV discordSRV;
private final PluginContainer pluginContainer;
private final Game game;
private final JarInJarClassLoader classLoader;
private final Path dataDirectory;
public DiscordSRVSpongeBootstrap(PluginContainer pluginContainer, Game game, JarInJarClassLoader classLoader, Path dataDirectory) throws IOException {
// Don't change these parameters
super(classLoader);
this.logger = new Log4JLoggerImpl(pluginContainer.logger());
this.dependencies = new InitialDependencyLoader(
logger,
dataDirectory,
new String[] {"dependencies/runtimeDownloadApi-sponge.txt"},
new JarInJarClasspathAppender(classLoader)
);
dependencies.whenComplete(() ->
this.discordSRV = new SpongeDiscordSRV(
pluginContainer,
game,
classLoader,
dataDirectory
)
);
this.pluginContainer = pluginContainer;
this.game = game;
this.classLoader = classLoader;
this.dataDirectory = dataDirectory;
}
@Override
public void onConstruct() {
dependencies.whenComplete(discordSRV::invokeEnable);
// Wait until dependencies ready, then initialize DiscordSRV
dependencies.join();
this.discordSRV = new SpongeDiscordSRV(logger, pluginContainer, game, classLoader, dataDirectory);
dependencies.runWhenComplete(discordSRV::invokeEnable);
}
@Override
public void onStarted() {
dependencies.whenComplete(discordSRV::invokeServerStarted);
dependencies.runWhenComplete(discordSRV::invokeServerStarted);
}
@Override
public void onRefresh() {
dependencies.whenComplete(discordSRV::invokeReload);
dependencies.runWhenComplete(discordSRV::invokeReload);
}
@Override
public void onStopping() {
dependencies.whenComplete(discordSRV::invokeDisable);
dependencies.runWhenComplete(discordSRV::invokeDisable);
}
}

View File

@ -24,7 +24,6 @@ import com.discordsrv.common.config.main.MainConfig;
import com.discordsrv.common.config.manager.ConnectionConfigManager;
import com.discordsrv.common.config.manager.MainConfigManager;
import com.discordsrv.common.logging.logger.Logger;
import com.discordsrv.common.logging.logger.impl.Log4JLoggerImpl;
import com.discordsrv.common.server.ServerDiscordSRV;
import com.discordsrv.sponge.console.SpongeConsole;
import com.discordsrv.sponge.player.SpongePlayerProvider;
@ -49,15 +48,17 @@ public class SpongeDiscordSRV extends ServerDiscordSRV<MainConfig, ConnectionCon
private final SpongeConsole console;
private final SpongePlayerProvider playerProvider;
public SpongeDiscordSRV(PluginContainer pluginContainer, Game game, JarInJarClassLoader classLoader, Path dataDirectory) {
public SpongeDiscordSRV(Logger logger, PluginContainer pluginContainer, Game game, JarInJarClassLoader classLoader, Path dataDirectory) {
this.pluginContainer = pluginContainer;
this.game = game;
this.logger = new Log4JLoggerImpl(pluginContainer.logger());
this.logger = logger;
this.dataDirectory = dataDirectory;
this.scheduler = new SpongeScheduler(this);
this.console = new SpongeConsole(this);
this.playerProvider = new SpongePlayerProvider(this);
load();
}
public PluginContainer container() {

View File

@ -19,6 +19,8 @@
package com.discordsrv.velocity;
import com.discordsrv.common.dependency.InitialDependencyLoader;
import com.discordsrv.common.logging.logger.Logger;
import com.discordsrv.common.logging.logger.impl.SLF4JLoggerImpl;
import com.google.inject.Inject;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
@ -29,7 +31,6 @@ import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.plugin.annotation.DataDirectory;
import com.velocitypowered.api.proxy.ProxyServer;
import dev.vankka.mcdependencydownload.velocity.classpath.VelocityClasspathAppender;
import org.slf4j.Logger;
import java.io.IOException;
import java.nio.file.Path;
@ -44,32 +45,44 @@ import java.nio.file.Path;
)
public class DiscordSRVVelocityBootstrap {
private final Logger logger;
private final InitialDependencyLoader dependencies;
private final ProxyServer proxyServer;
private final PluginContainer pluginContainer;
private final Path dataDirectory;
private VelocityDiscordSRV discordSRV;
@Inject
public DiscordSRVVelocityBootstrap(Logger logger, ProxyServer proxyServer, PluginContainer pluginContainer, @DataDirectory Path dataDirectory) throws IOException {
public DiscordSRVVelocityBootstrap(org.slf4j.Logger logger, ProxyServer proxyServer, PluginContainer pluginContainer, @DataDirectory Path dataDirectory) throws IOException {
this.logger = new SLF4JLoggerImpl(logger);
this.dependencies = new InitialDependencyLoader(
this.logger,
dataDirectory,
new String[] {"dependencies/runtimeDownloadApi-velocity.txt"},
new VelocityClasspathAppender(this, proxyServer)
);
dependencies.whenComplete(() -> this.discordSRV = new VelocityDiscordSRV(this, pluginContainer, proxyServer, logger, dataDirectory));
this.proxyServer = proxyServer;
this.pluginContainer = pluginContainer;
this.dataDirectory = dataDirectory;
}
@Subscribe
public void onProxyInitialize(ProxyInitializeEvent event) {
dependencies.whenComplete(discordSRV::invokeEnable);
// Wait until dependencies ready, then initialize DiscordSRV
dependencies.join();
this.discordSRV = new VelocityDiscordSRV(this, logger, proxyServer, pluginContainer, dataDirectory);
dependencies.runWhenComplete(discordSRV::invokeEnable);
}
@Subscribe
public void onProxyReload(ProxyReloadEvent event) {
dependencies.whenComplete(discordSRV::invokeReload);
dependencies.runWhenComplete(discordSRV::invokeReload);
}
@Subscribe
public void onProxyShutdown(ProxyShutdownEvent event) {
dependencies.whenComplete(discordSRV::invokeDisable);
dependencies.runWhenComplete(discordSRV::invokeDisable);
}
}

View File

@ -23,7 +23,6 @@ import com.discordsrv.common.config.main.MainConfig;
import com.discordsrv.common.config.manager.ConnectionConfigManager;
import com.discordsrv.common.config.manager.MainConfigManager;
import com.discordsrv.common.logging.logger.Logger;
import com.discordsrv.common.logging.logger.impl.SLF4JLoggerImpl;
import com.discordsrv.common.proxy.ProxyDiscordSRV;
import com.discordsrv.common.scheduler.StandardScheduler;
import com.discordsrv.velocity.console.VelocityConsole;
@ -37,8 +36,8 @@ import java.nio.file.Path;
public class VelocityDiscordSRV extends ProxyDiscordSRV<MainConfig, ConnectionConfig> {
private final Object plugin;
private final PluginContainer pluginContainer;
private final ProxyServer proxyServer;
private final PluginContainer pluginContainer;
private final Logger logger;
private final Path dataDirectory;
@ -46,16 +45,18 @@ public class VelocityDiscordSRV extends ProxyDiscordSRV<MainConfig, ConnectionCo
private final VelocityConsole console;
private final VelocityPlayerProvider playerProvider;
public VelocityDiscordSRV(Object plugin, PluginContainer pluginContainer, ProxyServer proxyServer, org.slf4j.Logger logger, Path dataDirectory) {
public VelocityDiscordSRV(Object plugin, Logger logger, ProxyServer proxyServer, PluginContainer pluginContainer, Path dataDirectory) {
this.plugin = plugin;
this.pluginContainer = pluginContainer;
this.proxyServer = proxyServer;
this.logger = new SLF4JLoggerImpl(logger);
this.pluginContainer = pluginContainer;
this.logger = logger;
this.dataDirectory = dataDirectory;
this.scheduler = new StandardScheduler(this);
this.console = new VelocityConsole(this);
this.playerProvider = new VelocityPlayerProvider(this);
load();
}
public Object plugin() {